def _reinit_database(self):
     # Close database
     self.ctl.dispose()
     # Destroy configuration folder
     shutil.rmtree(self.nxdrive_conf_folder_1)
     os.mkdir(self.nxdrive_conf_folder_1)
     # Recreate a controller
     self.ctl = Controller(self.nxdrive_conf_folder_1)
     self.ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                     self.user_1, self.password_1)
     self.ctl.bind_root(self.local_nxdrive_folder_1, self.workspace)
     self.syn = self.ctl.synchronizer
示例#2
0
    def handle(self, args):
        # use the CLI parser to check that the first args is a valid command
        options = self.parser.parse_args(args)
        if options.debug:
            # Install Post-Mortem debugger hook

            def info(type, value, tb):
                traceback.print_exception(type, value, tb)
                print
                debugger.pm()

            sys.excepthook = info

        filename = options.log_filename
        if filename is None:
            filename = os.path.join(
                options.nxdrive_home, 'logs', 'nxdrive.log')

        configure(
            filename,
            file_level=options.log_level_file,
            console_level=options.log_level_console,
            process_name=options.command,
        )
        self.controller = Controller(options.nxdrive_home)

        handler = getattr(self, options.command, None)
        if handler is None:
            raise NotImplementedError(
                'No handler implemented for command ' + options.command)
        return handler(options)
 def _reinit_database(self):
     # Close database
     self.ctl.dispose()
     # Destroy configuration folder
     shutil.rmtree(self.nxdrive_conf_folder_1)
     os.mkdir(self.nxdrive_conf_folder_1)
     # Recreate a controller
     self.ctl = Controller(self.nxdrive_conf_folder_1)
     self.ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                     self.user_1, self.password_1)
     self.ctl.bind_root(self.local_nxdrive_folder_1, self.workspace)
     self.syn = self.ctl.synchronizer
示例#4
0
 def get_controller(self, options):
     nb_tries = 1
     while (nb_tries <= GET_CTL_MAX_NB_TRIES):
         try:
             return Controller(options.nxdrive_home,
                         handshake_timeout=options.handshake_timeout,
                         timeout=options.timeout,
                         max_errors=options.max_errors,
                         update_url=options.update_site_url)
         except OperationalError as e:
             self.log.error("OperationalError during try #%d to get"
                            " controller, waiting for %d seconds and"
                            " retrying at most %d times" %
                            (nb_tries, GET_CTL_SLEEP_DURATION,
                             GET_CTL_MAX_NB_TRIES - nb_tries),
                            exc_info=True)
             time.sleep(GET_CTL_SLEEP_DURATION)
             nb_tries += 1
     if nb_tries > 1:
         raise e
    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()
class TestIntegrationReinitDatabase(IntegrationTestCase):

    def setUp(self):
        super(TestIntegrationReinitDatabase, self).setUp()
        self.syn, self.local, self.remote = self.init_default_drive()
        self.ctl = self.controller_1
        # Make a folder and a file
        self.remote.make_folder('/', 'Test folder')
        self.remote.make_file('/Test folder', 'Test.txt',
                              'This is some content')
        self.test_remote_folder_id = self.remote.get_info('/Test folder').uid
        # Wait for synchro
        self._synchronize()
        # Verify that all is synchronize
        self.assertTrue(self.local.exists('/Test folder'),
                        'Local folder should exist')
        self.assertTrue(self.local.exists('/Test folder/Test.txt'),
                        'Local file should exist')
        # Destroy database
        self._reinit_database()

    def tearDown(self):
        # Dispose dedicated Controller instantiated for this test
        # in _reinit_database()
        self.ctl.dispose()

        super(TestIntegrationReinitDatabase, self).tearDown()

    def _check_states(self):
        rows = self.controller_1.get_session().query(LastKnownState).all()
        for row in rows:
            self.assertEquals(row.pair_state, 'synchronized')

    def _reinit_database(self):
        # Close database
        self.ctl.dispose()
        # Destroy configuration folder
        shutil.rmtree(self.nxdrive_conf_folder_1)
        os.mkdir(self.nxdrive_conf_folder_1)
        # Recreate a controller
        self.ctl = Controller(self.nxdrive_conf_folder_1)
        self.ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                        self.user_1, self.password_1)
        self.ctl.bind_root(self.local_nxdrive_folder_1, self.workspace)
        self.syn = self.ctl.synchronizer

    def _synchronize(self, loops=1):
        super(TestIntegrationReinitDatabase, self)._synchronize(self.syn,
                                                                loops)

    def _check_conflict_locally_handled(self):
        # As a conflict has been raised 2 files should be present locally
        self.assertEqual(len(self.local.get_children_info("/Test folder")), 2)
        self.assertEqual(len(self.remote.get_children_info(
                                            self.test_remote_folder_id)), 1)

    def _check_conflict_locally_and_remotely_handled(self):
        # End the conflict handling by uploading the second local file to
        # the server
        self.assertEqual(len(self.local.get_children_info("/Test folder")), 2)
        self.assertEqual(len(self.remote.get_children_info(
                                            self.test_remote_folder_id,
                                            types=('File', 'Note'))), 2)

    def test_synchronize_folderish_and_same_digest(self):
        # Reload sync
        self._synchronize()
        # Check everything is synchronized
        self._check_states()

    def test_synchronize_remote_change(self):
        # Modify the remote file
        self.remote.update_content('/Test folder/Test.txt',
                                   'Content has changed')
        # Sync
        self._synchronize()
        # Check a conflict is detected and handled locally
        self._check_conflict_locally_handled()
        # Assert content of the original file has changed
        self.assertEquals(self.local.get_content('/Test folder/Test.txt'),
                          'Content has changed',
                          'Local content should be the same as the remote one')
        # Sync again
        self._synchronize(3)
        # Check a conflict is handled locally and remotely
        self._check_conflict_locally_and_remotely_handled()
        # Check everything is synchronized
        self._check_states()

    def test_synchronize_local_change(self):
        # Modify the local file
        time.sleep(self.OS_STAT_MTIME_RESOLUTION)
        self.local.update_content('/Test folder/Test.txt',
                                   'Content has changed')
        # Sync
        self._synchronize()
        # Check a conflict is detected and handled locally
        self._check_conflict_locally_handled()
        # Assert content of the original file has not changed
        self.assertEquals(self.local.get_content('/Test folder/Test.txt'),
                          'This is some content',
                          'Local content should be the same as the remote one')
        # Sync again
        self._synchronize(3)
        # Check a conflict is handled locally and remotely
        self._check_conflict_locally_and_remotely_handled()
        # Assert content has changed
        self.assertEquals(self.remote.get_content('/Test folder/Test.txt'),
                          'This is some content',
                          'Remote content should not have changed')
        # Check everything is synchronized
        self._check_states()

    def test_synchronize_remote_and_local_change(self):
        # Modify the remote file
        self.remote.update_content('/Test folder/Test.txt',
                                   'Content has remote changed')
        # Modify the local file
        time.sleep(self.OS_STAT_MTIME_RESOLUTION)
        self.local.update_content('/Test folder/Test.txt',
                                   'Content has local changed')
        # Sync
        self._synchronize()
        # Check a conflict is detected and handled locally
        self._check_conflict_locally_handled()
        # Assert content of the original file has not changed
        self.assertEquals(self.local.get_content('/Test folder/Test.txt'),
                          'Content has remote changed',
                          'Local content should be the same as remote one')
        # Sync again
        self._synchronize(3)
        # Check a conflict is handled locally and remotely
        self._check_conflict_locally_and_remotely_handled()
        # Assert content has changed
        self.assertEquals(self.local.get_content('/Test folder/Test.txt'),
                        'Content has remote changed',
                        'Remote changed content should not have changed again')
        # Check everything is synchronized
        self._check_states()
示例#7
0
class IntegrationTestCase(unittest.TestCase):

    TEST_WORKSPACE_PATH = (
        u'/default-domain/workspaces/nuxeo-drive-test-workspace')
    FS_ITEM_ID_PREFIX = u'defaultFileSystemItemFactory#default#'

    EMPTY_DIGEST = hashlib.md5().hexdigest()
    SOME_TEXT_CONTENT = b"Some text content."
    SOME_TEXT_DIGEST = hashlib.md5(SOME_TEXT_CONTENT).hexdigest()

    # 1s time resolution as we truncate remote last modification time to the
    # seconds in RemoteFileSystemClient.file_to_info() because of the datetime
    # resolution of some databases (MySQL...)
    REMOTE_MODIFICATION_TIME_RESOLUTION = 1.0

    # 1s time resolution because of the datetime resolution of MYSQL
    AUDIT_CHANGE_FINDER_TIME_RESOLUTION = 1.0

    # 1s resolution on HFS+ on OSX
    # 2s resolution on FAT but can be ignored as no Jenkins is running the test
    # suite under windows on FAT partitions
    # ~0.01s resolution for NTFS
    # 0.001s for EXT4FS
    OS_STAT_MTIME_RESOLUTION = 1.0

    # Nuxeo max length for document name
    DOC_NAME_MAX_LENGTH = 24

    def _synchronize(self, syn, delay=0.1, loops=1):
        self.wait_audit_change_finder_if_needed()
        self.wait()
        syn.loop(delay=delay, max_loops=loops)

    def setUp(self):
        # Check the Nuxeo server test environment
        self.nuxeo_url = os.environ.get('NXDRIVE_TEST_NUXEO_URL')
        self.admin_user = os.environ.get('NXDRIVE_TEST_USER')
        self.password = os.environ.get('NXDRIVE_TEST_PASSWORD')

        # Take default parameter if none has been set
        if self.nuxeo_url is None:
            self.nuxeo_url = "http://localhost:8080/nuxeo"
        if self.admin_user is None:
            self.admin_user = "******"
        if self.password is None:
            self.password = "******"

        if None in (self.nuxeo_url, self.admin_user, self.password):
            raise unittest.SkipTest(
                "No integration server configuration found in environment.")

        # Check the local filesystem test environment
        self.local_test_folder_1 = tempfile.mkdtemp(u'-nxdrive-tests-user-1')
        self.local_test_folder_2 = tempfile.mkdtemp(u'-nxdrive-tests-user-2')

        self.local_nxdrive_folder_1 = os.path.join(
            self.local_test_folder_1, u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_1)
        self.local_nxdrive_folder_2 = os.path.join(
            self.local_test_folder_2, u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_2)

        self.nxdrive_conf_folder_1 = os.path.join(
            self.local_test_folder_1, u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_1)

        self.nxdrive_conf_folder_2 = os.path.join(
            self.local_test_folder_2, u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_2)

        # Set echo to True to enable SQL statements and transactions logging
        # and echo_pool to True to enable connection pool logging
        self.controller_1 = Controller(self.nxdrive_conf_folder_1,
                                       echo=False, echo_pool=False)
        self.controller_2 = Controller(self.nxdrive_conf_folder_2,
                                       echo=False, echo_pool=False)
        self.controller_1.synchronizer.test_delay = 3
        self.controller_2.synchronizer.test_delay = 3
        self.version = self.controller_1.get_version()

        # Long timeout for the root client that is responsible for the test
        # environment set: this client is doing the first query on the Nuxeo
        # server and might need to wait for a long time without failing for
        # Nuxeo to finish initialize the repo on the first request after
        # startup
        root_remote_client = RemoteDocumentClient(
            self.nuxeo_url, self.admin_user,
            u'nxdrive-test-administrator-device', self.version,
            password=self.password, base_folder=u'/', timeout=60)

        # Call the Nuxeo operation to setup the integration test environment
        credentials = root_remote_client.execute(
            "NuxeoDrive.SetupIntegrationTests",
            userNames="user_1, user_2", permission='ReadWrite')

        credentials = [c.strip().split(u":") for c in credentials.split(u",")]
        self.user_1, self.password_1 = credentials[0]
        self.user_2, self.password_2 = credentials[1]

        ws_info = root_remote_client.fetch(self.TEST_WORKSPACE_PATH)
        self.workspace = ws_info[u'uid']
        self.workspace_title = ws_info[u'title']

        # Document client to be used to create remote test documents
        # and folders
        self.upload_tmp_dir = tempfile.mkdtemp(u'-nxdrive-uploads')
        remote_document_client_1 = RemoteDocumentClient(
            self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
            self.version,
            password=self.password_1, base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        remote_document_client_2 = RemoteDocumentClient(
            self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
            self.version,
            password=self.password_2, base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        # File system client to be used to create remote test documents
        # and folders
        remote_file_system_client_1 = RemoteFileSystemClient(
            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_file_system_client_2 = RemoteFileSystemClient(
            self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
            self.version,
            password=self.password_2, upload_tmp_dir=self.upload_tmp_dir)

        self.root_remote_client = root_remote_client
        self.remote_document_client_1 = remote_document_client_1
        self.remote_document_client_2 = remote_document_client_2
        self.remote_file_system_client_1 = remote_file_system_client_1
        self.remote_file_system_client_2 = remote_file_system_client_2

    def tearDown(self):
        # Force to clean all observers
        self.controller_1.synchronizer.stop_observers(raise_on_error=False)
        self.controller_2.synchronizer.stop_observers(raise_on_error=False)
        # Note that unbinding a server revokes the related token if needed,
        # see Controller.unbind_server()
        self.controller_1.unbind_all()
        self.controller_2.unbind_all()
        # Don't need to revoke tokens for the file system remote clients
        # since they use the same users as the remote document clients
        self.root_remote_client.execute("NuxeoDrive.TearDownIntegrationTests")

        if os.path.exists(self.upload_tmp_dir):
            shutil.rmtree(safe_long_path(self.upload_tmp_dir))

        if os.path.exists(self.local_test_folder_1):
            self.controller_1.dispose()
            try:
                shutil.rmtree(safe_long_path(self.local_test_folder_1))
            except:
                pass

        if os.path.exists(self.local_test_folder_2):
            self.controller_2.dispose()
            try:
                shutil.rmtree(safe_long_path(self.local_test_folder_2))
            except:
                pass

    def get_all_states(self, session=None, get_pair_state=False):
        """Utility to quickly introspect the current known states"""
        if session is None:
            session = self.controller_1.get_session()
        pairs = session.query(LastKnownState).order_by(
            LastKnownState.local_path,
            LastKnownState.remote_parent_path,
            LastKnownState.remote_name).all()
        if not get_pair_state:
            return [(p.local_path, p.local_state, p.remote_state)
                    for p in pairs]
        else:
            return [(p.local_path, p.local_state, p.remote_state, p.pair_state)
                    for p in pairs]

    def make_server_tree(self):
        remote_client = self.remote_document_client_1
        # create some folders on the server
        folder_1 = remote_client.make_folder(self.workspace, u'Folder 1')
        folder_1_1 = remote_client.make_folder(folder_1, u'Folder 1.1')
        folder_1_2 = remote_client.make_folder(folder_1, u'Folder 1.2')
        folder_2 = remote_client.make_folder(self.workspace, u'Folder 2')

        # create some files on the server
        remote_client.make_file(folder_2, u'Duplicated File.txt',
                                content=b"Some content.")
        remote_client.make_file(folder_2, u'Duplicated File.txt',
                                content=b"Other content.")

        remote_client.make_file(folder_1, u'File 1.txt', content=b"aaa")
        remote_client.make_file(folder_1_1, u'File 2.txt', content=b"bbb")
        remote_client.make_file(folder_1_2, u'File 3.txt', content=b"ccc")
        remote_client.make_file(folder_2, u'File 4.txt', content=b"ddd")
        remote_client.make_file(self.workspace, u'File 5.txt', content=b"eee")

    def init_default_drive(self):
        # Bind the server and root workspace
        ctl = self.controller_1
        ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                        self.user_1, self.password_1)
        ctl.bind_root(self.local_nxdrive_folder_1, self.workspace)

        # Launch first synchronization
        self.wait_audit_change_finder_if_needed()
        self.wait()
        syn = ctl.synchronizer
        syn.loop(delay=0.1, max_loops=1)

        # Get local and remote clients
        local = LocalClient(os.path.join(self.local_nxdrive_folder_1,
                                         self.workspace_title))
        remote = self.remote_document_client_1
        return syn, local, remote

    def wait(self):
        self.root_remote_client.wait()

    def wait_audit_change_finder_if_needed(self):
        if not self.root_remote_client.is_event_log_id_available():
            time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION)
示例#8
0
        dialog.show_message(msg, tab_index=tab_index)
        return False

    if app is None:
        log.debug("Launching Qt prompt to manage settings.")
        QtGui.QApplication([])
    dialog = Dialog(sb_field_spec, proxy_field_spec, version,
                    title="Nuxeo Drive - Settings",
                    callback=validate)
    is_dialog_open = True
    try:
        dialog.exec_()
    except:
        dialog.reject()
        raise
    finally:
        is_dialog_open = False
    return dialog.accepted

if __name__ == '__main__':
    from nxdrive.controller import Controller
    from nxdrive.controller import default_nuxeo_drive_folder
    ctl = Controller('/tmp')
    sb_settings = ServerBindingSettings(
                                    server_url='http://localhost:8080/nuxeo',
                                    username='******',
                                    local_folder=default_nuxeo_drive_folder())
    proxy_settings = ProxySettings()
    version = ctl.get_version()
    print prompt_settings(ctl, sb_settings, proxy_settings, version)
示例#9
0
    def setUp(self):
        # Check the Nuxeo server test environment
        self.nuxeo_url = os.environ.get('NXDRIVE_TEST_NUXEO_URL')
        self.admin_user = os.environ.get('NXDRIVE_TEST_USER')
        self.password = os.environ.get('NXDRIVE_TEST_PASSWORD')

        # Take default parameter if none has been set
        if self.nuxeo_url is None:
            self.nuxeo_url = "http://localhost:8080/nuxeo"
        if self.admin_user is None:
            self.admin_user = "******"
        if self.password is None:
            self.password = "******"

        if None in (self.nuxeo_url, self.admin_user, self.password):
            raise unittest.SkipTest(
                "No integration server configuration found in environment.")

        # Check the local filesystem test environment
        self.local_test_folder_1 = tempfile.mkdtemp(u'-nxdrive-tests-user-1')
        self.local_test_folder_2 = tempfile.mkdtemp(u'-nxdrive-tests-user-2')

        self.local_nxdrive_folder_1 = os.path.join(self.local_test_folder_1,
                                                   u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_1)
        self.local_nxdrive_folder_2 = os.path.join(self.local_test_folder_2,
                                                   u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_2)

        self.nxdrive_conf_folder_1 = os.path.join(self.local_test_folder_1,
                                                  u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_1)

        self.nxdrive_conf_folder_2 = os.path.join(self.local_test_folder_2,
                                                  u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_2)

        # Set echo to True to enable SQL statements and transactions logging
        # and echo_pool to True to enable connection pool logging
        self.controller_1 = Controller(self.nxdrive_conf_folder_1,
                                       echo=False,
                                       echo_pool=False)
        self.controller_2 = Controller(self.nxdrive_conf_folder_2,
                                       echo=False,
                                       echo_pool=False)
        self.controller_1.synchronizer.test_delay = 3
        self.controller_2.synchronizer.test_delay = 3
        self.version = self.controller_1.get_version()

        # Long timeout for the root client that is responsible for the test
        # environment set: this client is doing the first query on the Nuxeo
        # server and might need to wait for a long time without failing for
        # Nuxeo to finish initialize the repo on the first request after
        # startup
        root_remote_client = RemoteDocumentClient(
            self.nuxeo_url,
            self.admin_user,
            u'nxdrive-test-administrator-device',
            self.version,
            password=self.password,
            base_folder=u'/',
            timeout=60)

        # Call the Nuxeo operation to setup the integration test environment
        credentials = root_remote_client.execute(
            "NuxeoDrive.SetupIntegrationTests",
            userNames="user_1, user_2",
            permission='ReadWrite')

        credentials = [c.strip().split(u":") for c in credentials.split(u",")]
        self.user_1, self.password_1 = credentials[0]
        self.user_2, self.password_2 = credentials[1]

        ws_info = root_remote_client.fetch(self.TEST_WORKSPACE_PATH)
        self.workspace = ws_info[u'uid']
        self.workspace_title = ws_info[u'title']

        # Document client to be used to create remote test documents
        # and folders
        self.upload_tmp_dir = tempfile.mkdtemp(u'-nxdrive-uploads')
        remote_document_client_1 = RemoteDocumentClient(
            self.nuxeo_url,
            self.user_1,
            u'nxdrive-test-device-1',
            self.version,
            password=self.password_1,
            base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        remote_document_client_2 = RemoteDocumentClient(
            self.nuxeo_url,
            self.user_2,
            u'nxdrive-test-device-2',
            self.version,
            password=self.password_2,
            base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        # File system client to be used to create remote test documents
        # and folders
        remote_file_system_client_1 = RemoteFileSystemClient(
            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_file_system_client_2 = RemoteFileSystemClient(
            self.nuxeo_url,
            self.user_2,
            u'nxdrive-test-device-2',
            self.version,
            password=self.password_2,
            upload_tmp_dir=self.upload_tmp_dir)

        self.root_remote_client = root_remote_client
        self.remote_document_client_1 = remote_document_client_1
        self.remote_document_client_2 = remote_document_client_2
        self.remote_file_system_client_1 = remote_file_system_client_1
        self.remote_file_system_client_2 = remote_file_system_client_2
示例#10
0
 def get_controller(self, options):
     return Controller(options.nxdrive_home,
                       handshake_timeout=options.handshake_timeout,
                       timeout=options.timeout,
                       max_errors=options.max_errors)
# Useful to launch an interactive debugging session in ipython with %ed or %run
from nxdrive.controller import Controller
from nxdrive.model import ServerBinding
c = Controller('~/.nuxeo-drive')
s = c.get_session()
sb = s.query(ServerBinding).one()


示例#12
0
# Useful to launch an interactive debugging session in ipython with %ed or %run
from nxdrive.controller import Controller
from nxdrive.model import ServerBinding
c = Controller('~/.nuxeo-drive')
s = c.get_session()
sb = s.query(ServerBinding).one()
示例#13
0
class CliHandler(object):
    """Command Line Interface handler: parse options and execute operation"""

    def __init__(self):
        self.parser = make_cli_parser()

    def handle(self, args):
        # use the CLI parser to check that the first args is a valid command
        options = self.parser.parse_args(args)
        if options.debug:
            # Install Post-Mortem debugger hook

            def info(type, value, tb):
                traceback.print_exception(type, value, tb)
                print
                debugger.pm()

            sys.excepthook = info

        filename = options.log_filename
        if filename is None:
            filename = os.path.join(
                options.nxdrive_home, 'logs', 'nxdrive.log')

        configure(
            filename,
            file_level=options.log_level_file,
            console_level=options.log_level_console,
            process_name=options.command,
        )
        self.controller = Controller(options.nxdrive_home)

        handler = getattr(self, options.command, None)
        if handler is None:
            raise NotImplementedError(
                'No handler implemented for command ' + options.command)
        return handler(options)

    def start(self, options=None):
        self.controller.start()
        return 0

    def stop(self, options=None):
        self.controller.stop()
        return 0

    def console(self, options):

        fault_tolerant = not options.stop_on_error
        self.controller.loop(fault_tolerant=fault_tolerant,
                             delay=options.delay)
        return 0

    def status(self, options):
        states = self.controller.status(options.files)
        for filename, status in states:
            print status + '\t' + filename
        return 0

    def bind_server(self, options):
        if options.password is None:
            password = getpass()
        else:
            password = options.password
        self.controller.bind_server(options.local_folder, options.nuxeo_url,
                                    options.username, password)
        for root in options.remote_roots:
            self.controller.bind_root(options.local_folder, root,
                                      repository=options.remote_repo)
        return 0

    def unbind_server(self, options):
        self.controller.unbind_server(options.local_folder)
        return 0

    def bind_root(self, options):
        self.controller.bind_root(options.local_folder, options.remote_root,
                                  repository=options.remote_repo)
        return 0

    def unbind_root(self, options):
        self.controller.unbind_root(options.local_root)
        return 0

    def test(self, options):
        import nose
        # Monkeypatch nose usage message as it's complicated to include
        # the missing text resource in the frozen binary package
        nose.core.TestProgram.usage = lambda cls: ""
        argv = ['']

        if options.with_coverage:
            argv += [
                '--with-coverage',
                '--cover-package=nxdrive',
                '--cover-html',
                '--cover-html-dir=coverage',
            ]

        if options.with_profile:
            argv += [
                '--with-profile',
                '--profile-restrict=nxdrive',
            ]
        # List the test modules explicitly as recursive discovery is broken
        # when the app is frozen.
        argv += [
            "nxdrive.tests.test_controller",
            "nxdrive.tests.test_filesystem_client",
            "nxdrive.tests.test_integration_nuxeo_client",
            "nxdrive.tests.test_integration_synchronization",
        ]
        return 0 if nose.run(argv=argv) else 1
示例#14
0
class IntegrationTestCase(unittest.TestCase):

    TEST_WORKSPACE_PATH = (
        u'/default-domain/workspaces/nuxeo-drive-test-workspace')
    FS_ITEM_ID_PREFIX = u'defaultFileSystemItemFactory#default#'

    EMPTY_DIGEST = hashlib.md5().hexdigest()
    SOME_TEXT_CONTENT = b"Some text content."
    SOME_TEXT_DIGEST = hashlib.md5(SOME_TEXT_CONTENT).hexdigest()

    # 1s time resolution because of the datetime resolution of MYSQL
    AUDIT_CHANGE_FINDER_TIME_RESOLUTION = 1.0

    # 1s resolution on HFS+ on OSX
    # 2s resolution on FAT but can be ignored as no Jenkins is running the test
    # suite under windows on FAT partitions
    # ~0.01s resolution for NTFS
    # 0.001s for EXT4FS
    OS_STAT_MTIME_RESOLUTION = 1.0

    def setUp(self):
        # Check the Nuxeo server test environment
        self.nuxeo_url = os.environ.get('NXDRIVE_TEST_NUXEO_URL')
        self.admin_user = os.environ.get('NXDRIVE_TEST_USER')
        self.password = os.environ.get('NXDRIVE_TEST_PASSWORD')

        if None in (self.nuxeo_url, self.admin_user, self.password):
            raise unittest.SkipTest(
                "No integration server configuration found in environment.")

        # Long timeout for the root client that is responsible for the test
        # environment set: this client is doing the first query on the Nuxeo
        # server and might need to wait for a long time without failing for
        # Nuxeo to finish initialize the repo on the first request after
        # startup
        root_remote_client = RemoteDocumentClient(
            self.nuxeo_url, self.admin_user,
            u'nxdrive-test-administrator-device',
            password=self.password, base_folder=u'/', timeout=60)

        # Call the Nuxeo operation to setup the integration test environment
        credentials = root_remote_client.execute(
            "NuxeoDrive.SetupIntegrationTests",
            userNames="user_1, user_2")

        credentials = [c.strip().split(u":") for c in credentials.split(u",")]
        self.user_1, self.password_1 = credentials[0]
        self.user_2, self.password_2 = credentials[1]

        ws_info = root_remote_client.fetch(self.TEST_WORKSPACE_PATH)
        self.workspace = ws_info[u'uid']
        self.workspace_title = ws_info[u'title']

        # Document client to be used to create remote test documents
        # and folders
        self.upload_tmp_dir = tempfile.mkdtemp(u'-nxdrive-uploads')
        remote_document_client_1 = RemoteDocumentClient(
            self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
            password=self.password_1, base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        remote_document_client_2 = RemoteDocumentClient(
            self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
            password=self.password_2, base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        # File system client to be used to create remote test documents
        # and folders
        remote_file_system_client_1 = RemoteFileSystemClient(
            self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
            password=self.password_1, upload_tmp_dir=self.upload_tmp_dir)

        remote_file_system_client_2 = RemoteFileSystemClient(
            self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
            password=self.password_2, upload_tmp_dir=self.upload_tmp_dir)

        # Check the local filesystem test environment
        self.local_test_folder_1 = tempfile.mkdtemp(u'-nxdrive-tests-user-1')
        self.local_test_folder_2 = tempfile.mkdtemp(u'-nxdrive-tests-user-2')

        self.local_nxdrive_folder_1 = os.path.join(
            self.local_test_folder_1, u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_1)
        self.local_nxdrive_folder_2 = os.path.join(
            self.local_test_folder_2, u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_2)

        self.nxdrive_conf_folder_1 = os.path.join(
            self.local_test_folder_1, u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_1)

        self.nxdrive_conf_folder_2 = os.path.join(
            self.local_test_folder_2, u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_2)

        # Set echo to True to enable SQL statements logging
        self.controller_1 = Controller(self.nxdrive_conf_folder_1,
                                       echo=False)
        self.controller_2 = Controller(self.nxdrive_conf_folder_2,
                                       echo=False)
        self.root_remote_client = root_remote_client
        self.remote_document_client_1 = remote_document_client_1
        self.remote_document_client_2 = remote_document_client_2
        self.remote_file_system_client_1 = remote_file_system_client_1
        self.remote_file_system_client_2 = remote_file_system_client_2

    def tearDown(self):
        self.controller_1.unbind_all()
        self.controller_2.unbind_all()
        self.remote_document_client_1.revoke_token()
        self.remote_document_client_2.revoke_token()
        # Don't need to revoke tokens for the file system remote clients
        # since they use the same users as the remote document clients
        self.root_remote_client.execute(u"NuxeoDrive.TearDownIntegrationTests")

        self.root_remote_client.revoke_token()

        if os.path.exists(self.upload_tmp_dir):
            shutil.rmtree(safe_long_path(self.upload_tmp_dir))

        if os.path.exists(self.local_test_folder_1):
            self.controller_1.dispose()
            shutil.rmtree(safe_long_path(self.local_test_folder_1))

        if os.path.exists(self.local_test_folder_2):
            self.controller_2.dispose()
            shutil.rmtree(safe_long_path(self.local_test_folder_2))

    def get_all_states(self, session=None):
        """Utility to quickly introspect the current known states"""
        if session is None:
            session = self.controller_1.get_session()
        pairs = session.query(LastKnownState).order_by(
            LastKnownState.local_path,
            LastKnownState.remote_parent_path,
            LastKnownState.remote_name).all()
        return [(p.local_path, p.local_state, p.remote_state) for p in pairs]

    def make_server_tree(self):
        remote_client = self.remote_document_client_1
        # create some folders on the server
        folder_1 = remote_client.make_folder(self.workspace, u'Folder 1')
        folder_1_1 = remote_client.make_folder(folder_1, u'Folder 1.1')
        folder_1_2 = remote_client.make_folder(folder_1, u'Folder 1.2')
        folder_2 = remote_client.make_folder(self.workspace, u'Folder 2')

        # create some files on the server
        remote_client.make_file(folder_2, u'Duplicated File.txt',
                                content=b"Some content.")
        remote_client.make_file(folder_2, u'Duplicated File.txt',
                                content=b"Other content.")

        remote_client.make_file(folder_1, u'File 1.txt', content=b"aaa")
        remote_client.make_file(folder_1_1, u'File 2.txt', content=b"bbb")
        remote_client.make_file(folder_1_2, u'File 3.txt', content=b"ccc")
        remote_client.make_file(folder_2, u'File 4.txt', content=b"ddd")
        remote_client.make_file(self.workspace, u'File 5.txt', content=b"eee")

    def wait(self):
        self.root_remote_client.wait()
class TestIntegrationReinitDatabase(IntegrationTestCase):

    def setUp(self):
        super(TestIntegrationReinitDatabase, self).setUp()
        self.syn, self.local, self.remote = self.init_default_drive()
        self.ctl = self.controller_1
        # Make a folder and a file
        self.remote.make_folder('/', 'Test folder')
        self.remote.make_file('/Test folder', 'Test.txt',
                              'This is some content')
        self.test_remote_folder_id = self.remote.get_info('/Test folder').uid
        # Wait for synchro
        self._synchronize()
        # Verify that all is synchronize
        self.assertTrue(self.local.exists('/Test folder'),
                        'Local folder should exist')
        self.assertTrue(self.local.exists('/Test folder/Test.txt'),
                        'Local file should exist')
        # Destroy database
        self._reinit_database()

    def tearDown(self):
        # Dispose dedicated Controller instantiated for this test
        # in _reinit_database()
        self.ctl.dispose()

        super(TestIntegrationReinitDatabase, self).tearDown()

    def _check_states(self):
        rows = self.controller_1.get_session().query(LastKnownState).all()
        for row in rows:
            self.assertEquals(row.pair_state, 'synchronized')

    def _reinit_database(self):
        # Close database
        self.ctl.dispose()
        # Destroy configuration folder
        shutil.rmtree(self.nxdrive_conf_folder_1)
        os.mkdir(self.nxdrive_conf_folder_1)
        # Recreate a controller
        self.ctl = Controller(self.nxdrive_conf_folder_1)
        self.ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                        self.user_1, self.password_1)
        self.ctl.bind_root(self.local_nxdrive_folder_1, self.workspace)
        self.syn = self.ctl.synchronizer

    def _synchronize(self, loops=1):
        super(TestIntegrationReinitDatabase, self)._synchronize(self.syn, loops)

    def _check_conflict_locally_handled(self):
        # As a conflict has been raised 2 files should be present locally
        self.assertEqual(len(self.local.get_children_info("/Test folder")), 2)
        self.assertEqual(len(self.remote.get_children_info(
                                            self.test_remote_folder_id)), 1)

    def _check_conflict_locally_and_remotely_handled(self):
        # End the conflict handling by uploading the second local file to
        # the server
        self.assertEqual(len(self.local.get_children_info("/Test folder")), 2)
        self.assertEqual(len(self.remote.get_children_info(
                                            self.test_remote_folder_id,
                                            types=('File', 'Note'))), 2)

    def test_synchronize_folderish_and_same_digest(self):
        # Reload sync
        self._synchronize()
        # Check everything is synchronized
        self._check_states()

    def test_synchronize_remote_change(self):
        # Modify the remote file
        self.remote.update_content('/Test folder/Test.txt',
                                   'Content has changed')
        # Sync
        self._synchronize()
        # Check a conflict is detected and handled locally
        self._check_conflict_locally_handled()
        # Assert content of the original file has changed
        self.assertEquals(self.local.get_content('/Test folder/Test.txt'),
                          'Content has changed',
                          'Local content should be the same as the remote one')
        # Sync again
        self._synchronize(3)
        # Check a conflict is handled locally and remotely
        self._check_conflict_locally_and_remotely_handled()
        # Check everything is synchronized
        self._check_states()

    def test_synchronize_local_change(self):
        # Modify the local file
        time.sleep(self.OS_STAT_MTIME_RESOLUTION)
        self.local.update_content('/Test folder/Test.txt',
                                   'Content has changed')
        # Sync
        self._synchronize()
        # Check a conflict is detected and handled locally
        self._check_conflict_locally_handled()
        # Assert content of the original file has not changed
        self.assertEquals(self.local.get_content('/Test folder/Test.txt'),
                          'This is some content',
                          'Local content should be the same as the remote one')
        # Sync again
        self._synchronize(3)
        # Check a conflict is handled locally and remotely
        self._check_conflict_locally_and_remotely_handled()
        # Assert content has changed
        self.assertEquals(self.remote.get_content('/Test folder/Test.txt'),
                          'This is some content',
                          'Remote content should not have changed')
        # Check everything is synchronized
        self._check_states()

    def test_synchronize_remote_and_local_change(self):
        # Modify the remote file
        self.remote.update_content('/Test folder/Test.txt',
                                   'Content has remote changed')
        # Modify the local file
        time.sleep(self.OS_STAT_MTIME_RESOLUTION)
        self.local.update_content('/Test folder/Test.txt',
                                   'Content has local changed')
        # Sync
        self._synchronize()
        # Check a conflict is detected and handled locally
        self._check_conflict_locally_handled()
        # Assert content of the original file has not changed
        self.assertEquals(self.local.get_content('/Test folder/Test.txt'),
                          'Content has remote changed',
                          'Local content should be the same as remote one')
        # Sync again
        self._synchronize(3)
        # Check a conflict is handled locally and remotely
        self._check_conflict_locally_and_remotely_handled()
        # Assert content has changed
        self.assertEquals(self.local.get_content('/Test folder/Test.txt'),
                        'Content has remote changed',
                        'Remote changed content should not have changed again')
        # Check everything is synchronized
        self._check_states()
示例#16
0
    def setUp(self):
        # Check the Nuxeo server test environment
        self.nuxeo_url = os.environ.get('NXDRIVE_TEST_NUXEO_URL')
        self.admin_user = os.environ.get('NXDRIVE_TEST_USER')
        self.password = os.environ.get('NXDRIVE_TEST_PASSWORD')

        if None in (self.nuxeo_url, self.admin_user, self.password):
            raise unittest.SkipTest(
                "No integration server configuration found in environment.")

        # Long timeout for the root client that is responsible for the test
        # environment set: this client is doing the first query on the Nuxeo
        # server and might need to wait for a long time without failing for
        # Nuxeo to finish initialize the repo on the first request after
        # startup
        root_remote_client = RemoteDocumentClient(
            self.nuxeo_url, self.admin_user,
            u'nxdrive-test-administrator-device',
            password=self.password, base_folder=u'/', timeout=60)

        # Call the Nuxeo operation to setup the integration test environment
        credentials = root_remote_client.execute(
            "NuxeoDrive.SetupIntegrationTests",
            userNames="user_1, user_2")

        credentials = [c.strip().split(u":") for c in credentials.split(u",")]
        self.user_1, self.password_1 = credentials[0]
        self.user_2, self.password_2 = credentials[1]

        ws_info = root_remote_client.fetch(self.TEST_WORKSPACE_PATH)
        self.workspace = ws_info[u'uid']
        self.workspace_title = ws_info[u'title']

        # Document client to be used to create remote test documents
        # and folders
        self.upload_tmp_dir = tempfile.mkdtemp(u'-nxdrive-uploads')
        remote_document_client_1 = RemoteDocumentClient(
            self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
            password=self.password_1, base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        remote_document_client_2 = RemoteDocumentClient(
            self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
            password=self.password_2, base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        # File system client to be used to create remote test documents
        # and folders
        remote_file_system_client_1 = RemoteFileSystemClient(
            self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
            password=self.password_1, upload_tmp_dir=self.upload_tmp_dir)

        remote_file_system_client_2 = RemoteFileSystemClient(
            self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
            password=self.password_2, upload_tmp_dir=self.upload_tmp_dir)

        # Check the local filesystem test environment
        self.local_test_folder_1 = tempfile.mkdtemp(u'-nxdrive-tests-user-1')
        self.local_test_folder_2 = tempfile.mkdtemp(u'-nxdrive-tests-user-2')

        self.local_nxdrive_folder_1 = os.path.join(
            self.local_test_folder_1, u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_1)
        self.local_nxdrive_folder_2 = os.path.join(
            self.local_test_folder_2, u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_2)

        self.nxdrive_conf_folder_1 = os.path.join(
            self.local_test_folder_1, u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_1)

        self.nxdrive_conf_folder_2 = os.path.join(
            self.local_test_folder_2, u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_2)

        # Set echo to True to enable SQL statements logging
        self.controller_1 = Controller(self.nxdrive_conf_folder_1,
                                       echo=False)
        self.controller_2 = Controller(self.nxdrive_conf_folder_2,
                                       echo=False)
        self.root_remote_client = root_remote_client
        self.remote_document_client_1 = remote_document_client_1
        self.remote_document_client_2 = remote_document_client_2
        self.remote_file_system_client_1 = remote_file_system_client_1
        self.remote_file_system_client_2 = remote_file_system_client_2
示例#17
0
class IntegrationTestCase(unittest.TestCase):

    TEST_WORKSPACE_PATH = (
        u'/default-domain/workspaces/nuxeo-drive-test-workspace')
    FS_ITEM_ID_PREFIX = u'defaultFileSystemItemFactory#default#'

    EMPTY_DIGEST = hashlib.md5().hexdigest()
    SOME_TEXT_CONTENT = b"Some text content."
    SOME_TEXT_DIGEST = hashlib.md5(SOME_TEXT_CONTENT).hexdigest()

    # 1s time resolution because of the datetime resolution of MYSQL
    AUDIT_CHANGE_FINDER_TIME_RESOLUTION = 1.0

    # 1s resolution on HFS+ on OSX
    # 2s resolution on FAT but can be ignored as no Jenkins is running the test
    # suite under windows on FAT partitions
    # ~0.01s resolution for NTFS
    # 0.001s for EXT4FS
    OS_STAT_MTIME_RESOLUTION = 1.0

    # Nuxeo max length for document name
    DOC_NAME_MAX_LENGTH = 24

    def _synchronize(self, syn, delay=0.1, loops=1):
        time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION)
        self.wait()
        syn.loop(delay=delay, max_loops=loops)

    def setUp(self):
        # Check the Nuxeo server test environment
        self.nuxeo_url = os.environ.get('NXDRIVE_TEST_NUXEO_URL')
        self.admin_user = os.environ.get('NXDRIVE_TEST_USER')
        self.password = os.environ.get('NXDRIVE_TEST_PASSWORD')

        # Take default parameter if none has been set
        if self.nuxeo_url is None:
            self.nuxeo_url = "http://localhost:8080/nuxeo"
        if self.admin_user is None:
            self.admin_user = "******"
        if self.password is None:
            self.password = "******"

        if None in (self.nuxeo_url, self.admin_user, self.password):
            raise unittest.SkipTest(
                "No integration server configuration found in environment.")

        # Check the local filesystem test environment
        self.local_test_folder_1 = tempfile.mkdtemp(u'-nxdrive-tests-user-1')
        self.local_test_folder_2 = tempfile.mkdtemp(u'-nxdrive-tests-user-2')

        self.local_nxdrive_folder_1 = os.path.join(self.local_test_folder_1,
                                                   u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_1)
        self.local_nxdrive_folder_2 = os.path.join(self.local_test_folder_2,
                                                   u'Nuxeo Drive')
        os.mkdir(self.local_nxdrive_folder_2)

        self.nxdrive_conf_folder_1 = os.path.join(self.local_test_folder_1,
                                                  u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_1)

        self.nxdrive_conf_folder_2 = os.path.join(self.local_test_folder_2,
                                                  u'nuxeo-drive-conf')
        os.mkdir(self.nxdrive_conf_folder_2)

        # Set echo to True to enable SQL statements and transactions logging
        # and echo_pool to True to enable connection pool logging
        self.controller_1 = Controller(self.nxdrive_conf_folder_1,
                                       echo=False,
                                       echo_pool=False)
        self.controller_2 = Controller(self.nxdrive_conf_folder_2,
                                       echo=False,
                                       echo_pool=False)
        self.controller_1.synchronizer.test_delay = 3
        self.controller_2.synchronizer.test_delay = 3
        self.version = self.controller_1.get_version()

        # Long timeout for the root client that is responsible for the test
        # environment set: this client is doing the first query on the Nuxeo
        # server and might need to wait for a long time without failing for
        # Nuxeo to finish initialize the repo on the first request after
        # startup
        root_remote_client = RemoteDocumentClient(
            self.nuxeo_url,
            self.admin_user,
            u'nxdrive-test-administrator-device',
            self.version,
            password=self.password,
            base_folder=u'/',
            timeout=60)

        # Call the Nuxeo operation to setup the integration test environment
        credentials = root_remote_client.execute(
            "NuxeoDrive.SetupIntegrationTests",
            userNames="user_1, user_2",
            permission='ReadWrite')

        credentials = [c.strip().split(u":") for c in credentials.split(u",")]
        self.user_1, self.password_1 = credentials[0]
        self.user_2, self.password_2 = credentials[1]

        ws_info = root_remote_client.fetch(self.TEST_WORKSPACE_PATH)
        self.workspace = ws_info[u'uid']
        self.workspace_title = ws_info[u'title']

        # Document client to be used to create remote test documents
        # and folders
        self.upload_tmp_dir = tempfile.mkdtemp(u'-nxdrive-uploads')
        remote_document_client_1 = RemoteDocumentClient(
            self.nuxeo_url,
            self.user_1,
            u'nxdrive-test-device-1',
            self.version,
            password=self.password_1,
            base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        remote_document_client_2 = RemoteDocumentClient(
            self.nuxeo_url,
            self.user_2,
            u'nxdrive-test-device-2',
            self.version,
            password=self.password_2,
            base_folder=self.workspace,
            upload_tmp_dir=self.upload_tmp_dir)

        # File system client to be used to create remote test documents
        # and folders
        remote_file_system_client_1 = RemoteFileSystemClient(
            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_file_system_client_2 = RemoteFileSystemClient(
            self.nuxeo_url,
            self.user_2,
            u'nxdrive-test-device-2',
            self.version,
            password=self.password_2,
            upload_tmp_dir=self.upload_tmp_dir)

        self.root_remote_client = root_remote_client
        self.remote_document_client_1 = remote_document_client_1
        self.remote_document_client_2 = remote_document_client_2
        self.remote_file_system_client_1 = remote_file_system_client_1
        self.remote_file_system_client_2 = remote_file_system_client_2

    def tearDown(self):
        # Force to clean all observers
        for observer in self.controller_1.synchronizer.observers:
            observer.stop()
            observer.join()
            del observer
        self.controller_1.synchronizer.observers = []
        for observer in self.controller_2.synchronizer.observers:
            observer.stop()
            observer.join()
            del observer
        self.controller_2.synchronizer.observers = []
        # Note that unbinding a server revokes the related token if needed,
        # see Controller.unbind_server()
        self.controller_1.unbind_all()
        self.controller_2.unbind_all()
        # Don't need to revoke tokens for the file system remote clients
        # since they use the same users as the remote document clients
        self.root_remote_client.execute("NuxeoDrive.TearDownIntegrationTests")

        if os.path.exists(self.upload_tmp_dir):
            shutil.rmtree(safe_long_path(self.upload_tmp_dir))

        if os.path.exists(self.local_test_folder_1):
            self.controller_1.dispose()
            try:
                shutil.rmtree(safe_long_path(self.local_test_folder_1))
            except:
                pass

        if os.path.exists(self.local_test_folder_2):
            self.controller_2.dispose()
            try:
                shutil.rmtree(safe_long_path(self.local_test_folder_2))
            except:
                pass

    def get_all_states(self, session=None):
        """Utility to quickly introspect the current known states"""
        if session is None:
            session = self.controller_1.get_session()
        pairs = session.query(LastKnownState).order_by(
            LastKnownState.local_path, LastKnownState.remote_parent_path,
            LastKnownState.remote_name).all()
        return [(p.local_path, p.local_state, p.remote_state) for p in pairs]

    def make_server_tree(self):
        remote_client = self.remote_document_client_1
        # create some folders on the server
        folder_1 = remote_client.make_folder(self.workspace, u'Folder 1')
        folder_1_1 = remote_client.make_folder(folder_1, u'Folder 1.1')
        folder_1_2 = remote_client.make_folder(folder_1, u'Folder 1.2')
        folder_2 = remote_client.make_folder(self.workspace, u'Folder 2')

        # create some files on the server
        remote_client.make_file(folder_2,
                                u'Duplicated File.txt',
                                content=b"Some content.")
        remote_client.make_file(folder_2,
                                u'Duplicated File.txt',
                                content=b"Other content.")

        remote_client.make_file(folder_1, u'File 1.txt', content=b"aaa")
        remote_client.make_file(folder_1_1, u'File 2.txt', content=b"bbb")
        remote_client.make_file(folder_1_2, u'File 3.txt', content=b"ccc")
        remote_client.make_file(folder_2, u'File 4.txt', content=b"ddd")
        remote_client.make_file(self.workspace, u'File 5.txt', content=b"eee")

    def init_default_drive(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
        return syn, local, remote

    def wait(self):
        self.root_remote_client.wait()