Skip to content
This repository has been archived by the owner on Feb 22, 2024. It is now read-only.

Commit

Permalink
[tests] Tests for api_session and repo; pep8 style changes.
Browse files Browse the repository at this point in the history
Signed-off-by: Xiangyu Bu <xybu92@live.com>
  • Loading branch information
xybu committed Jan 18, 2017
1 parent 37f11e8 commit bb4ce19
Show file tree
Hide file tree
Showing 17 changed files with 362 additions and 53 deletions.
6 changes: 5 additions & 1 deletion onedrived/od_api_session.py
Expand Up @@ -26,10 +26,14 @@ def save_session(self, **save_session_kwargs):
if self.SESSION_ARG_KEYNAME not in save_session_kwargs:
raise ValueError('"%s" must be specified in save_session() argument.' % self.SESSION_ARG_KEYNAME)
data = binascii.b2a_base64(zlib.compress(pickle.dumps(self, self.PICKLE_PROTOCOL)))
keyring.set_password(self.KEYRING_SERVICE_NAME, save_session_kwargs['key'], data)
keyring.set_password(self.KEYRING_SERVICE_NAME, save_session_kwargs[self.SESSION_ARG_KEYNAME], data)

@staticmethod
def load_session(**load_session_kwargs):
"""
:param dict[str, str] load_session_kwargs:
:return onedrived.od_api_session.OneDriveAPISession:
"""
keyarg = OneDriveAPISession.SESSION_ARG_KEYNAME
if keyarg not in load_session_kwargs:
raise ValueError('"%s" must be specified in load_session() argument.' % keyarg)
Expand Down
3 changes: 2 additions & 1 deletion onedrived/od_auth.py
Expand Up @@ -60,7 +60,8 @@ def get_profile(self, user_id='me', proxies=None):
Fetch basic profile of the specified user (Live ID).
:param str user_id: (Optional) ID of the target user.
:param dict[str, str] proxies: (Optional) Proxies to issue the HTTP request.
:return od_models.account_profile.OneDriveUserProfile: An OneDriveUserProfile object that od_models the user info.
:return od_models.account_profile.OneDriveUserProfile:
An OneDriveUserProfile object that od_models the user info.
"""
url = 'https://apis.live.net/v5.0/' + user_id
headers = {'Authorization': 'Bearer ' + self.client.auth_provider.access_token}
Expand Down
3 changes: 2 additions & 1 deletion onedrived/od_context.py
Expand Up @@ -143,7 +143,8 @@ def get_account(self, account_id):
"""
Return profile of a saved account.
:param str account_id: ID of the account to query.
:return od_models.account_profile.OneDriveAccountProfile: An OneDriveAccountProfile object of the account profile.
:return od_models.account_profile.OneDriveAccountProfile:
An OneDriveAccountProfile object of the account profile.
"""
return _account_profile.OneDriveAccountProfile(self.config['accounts'][account_id])

Expand Down
14 changes: 10 additions & 4 deletions onedrived/od_main.py
Expand Up @@ -25,16 +25,21 @@
task_pool = None


def join_workers():
od_threads.TaskWorkerThread.exit()
for w in task_workers:
if w:
w.join()


# noinspection PyUnusedLocal
def shutdown_callback(msg, code):
logging.info('Shutting down.')
context.loop.stop()
if task_pool:
task_pool.close(len(task_workers))
od_threads.TaskWorkerThread.exit()
for w in task_workers:
if w: w.join()
if context:
join_workers()
if context and context.watcher:
context.watcher.close()
try:
os.remove(pidfile)
Expand Down Expand Up @@ -128,5 +133,6 @@ def main():
finally:
context.loop.close()


if __name__ == '__main__':
main()
4 changes: 2 additions & 2 deletions onedrived/od_pref.py
Expand Up @@ -334,8 +334,8 @@ def set_drive(drive_id=None, email=None, local_root=None, ignore_file=None):
click.echo(click.style('Warning: ignore file path does not point to a file. Use default.', fg='yellow'))
ignore_file = context.config_dir + '/' + context.DEFAULT_IGNORE_FILENAME
if (drive_exists and
local_root == curr_drive_config.localroot_path and
ignore_file == curr_drive_config.ignorefile_path):
local_root == curr_drive_config.localroot_path and
ignore_file == curr_drive_config.ignorefile_path):
click.echo(click.style('No parameter was changed. Skipped operation.', fg='yellow'))
return
except ValueError as e:
Expand Down
20 changes: 8 additions & 12 deletions onedrived/od_repo.py
Expand Up @@ -86,6 +86,7 @@ def refresh_session(self):
self.context.loop.call_later(t, self.refresh_session)

def close(self):
logging.debug('Closing database "%s".', self._item_store_path)
self._conn.close()

def get_item_by_path(self, item_name, parent_relpath):
Expand Down Expand Up @@ -113,7 +114,7 @@ def delete_item(self, item_name, parent_relpath, is_folder=False):
if is_folder:
item_relpath = parent_relpath + '/' + item_name
cursor.execute('DELETE FROM items WHERE parent_path=? OR parent_path LIKE ?',
(item_relpath, item_relpath + '/%'))
(item_relpath, item_relpath + '/%'))
cursor.execute('DELETE FROM items WHERE parent_path=? AND name=?', (parent_relpath, item_name))

def move_item(self, item_name, parent_relpath, new_name, new_parent_relpath, is_folder=False):
Expand All @@ -128,16 +129,11 @@ def move_item(self, item_name, parent_relpath, new_name, new_parent_relpath, is_
if is_folder:
item_relpath = parent_relpath + '/' + item_name
cursor.execute('UPDATE items SET parent_path=? || substr(parent_path, ?) '
'WHERE parent_path=? OR parent_path LIKE ?',
(new_parent_relpath + '/' + new_name, len(item_relpath) + 1,
item_relpath, item_relpath + '/%'))
'WHERE parent_path=? OR parent_path LIKE ?',
(new_parent_relpath + '/' + new_name, len(item_relpath) + 1,
item_relpath, item_relpath + '/%'))
cursor.execute('UPDATE items SET parent_path=?, name=? WHERE parent_path=? AND name=?',
(new_parent_relpath, new_name, parent_relpath, item_name))

def update_status(self, item_name, parent_relpath, status=ItemRecordStatus.OK):
with self._lock, self._conn:
self._conn.execute('UPDATE items SET status=? WHERE parent_path=? AND name=?',
(status, parent_relpath, item_name))
(new_parent_relpath, new_name, parent_relpath, item_name))

def unmark_items(self, item_name, parent_relpath, is_folder=False):
"""
Expand All @@ -149,9 +145,9 @@ def unmark_items(self, item_name, parent_relpath, is_folder=False):
if is_folder:
item_relpath = parent_relpath + '/' + item_name
cursor.execute('UPDATE items SET status=? WHERE parent_path=? OR parent_path LIKE ?',
(ItemRecordStatus.OK, item_relpath, item_relpath + '/%'))
(ItemRecordStatus.OK, item_relpath, item_relpath + '/%'))
cursor.execute('UPDATE items SET status=? WHERE parent_path=? AND name=?',
(ItemRecordStatus.OK, parent_relpath, item_name))
(ItemRecordStatus.OK, parent_relpath, item_name))

def mark_all_items(self, mark=ItemRecordStatus.MARKED):
with self._lock, self._conn:
Expand Down
25 changes: 13 additions & 12 deletions onedrived/od_tasks/merge_dir.py
Expand Up @@ -6,6 +6,12 @@
from send2trash import send2trash

from . import base
from . import delete_item, download_file, upload_file
from .. import mkdir, fix_owner_and_timestamp
from ..od_api_helper import get_item_modified_datetime, item_request_call
from ..od_dateutils import datetime_to_timestamp, diff_timestamps
from ..od_hashutils import hash_match, sha1_value
from ..od_repo import ItemRecordType, ItemRecordStatus


class MergeDirectoryTask(base.TaskBase):
Expand Down Expand Up @@ -156,8 +162,8 @@ def get_local_sha1_hash():
return local_sha1_hash

if (remote_item.id == item_record.item_id and remote_item.c_tag == item_record.c_tag or
remote_item.size == item_record.size and
diff_timestamps(remote_mtime_ts, record_mtime_ts) == 0):
remote_item.size == item_record.size and
diff_timestamps(remote_mtime_ts, record_mtime_ts) == 0):
# The remote item metadata matches the database record. So this item has been synced before.
if item_stat is None:
# The local file was synced but now is gone. Delete remote one as well.
Expand All @@ -166,8 +172,8 @@ def get_local_sha1_hash():
self.task_pool.add_task(delete_item.DeleteRemoteItemTask(
self.repo, self.task_pool, self.rel_path, remote_item.name, remote_item.id, False))
elif (item_stat.st_size == item_record.size_local and
(diff_timestamps(local_mtime_ts, record_mtime_ts) == 0 or
remote_sha1_hash and remote_sha1_hash == get_local_sha1_hash())):
(diff_timestamps(local_mtime_ts, record_mtime_ts) == 0 or
remote_sha1_hash and remote_sha1_hash == get_local_sha1_hash())):
# If the local file matches the database record (i.e., same mtime timestamp or same content),
# simply return. This is the best case.
if diff_timestamps(local_mtime_ts, remote_mtime_ts) != 0:
Expand Down Expand Up @@ -199,7 +205,7 @@ def get_local_sha1_hash():
download_file.DownloadFileTask(self.repo, self.task_pool, remote_item, self.rel_path))
elif item_stat.st_size == item_record.size_local and \
(diff_timestamps(local_mtime_ts, record_mtime_ts) == 0 or
item_record.sha1_hash and item_record.sha1_hash == get_local_sha1_hash()):
item_record.sha1_hash and item_record.sha1_hash == get_local_sha1_hash()):
# Local file agrees with database record. This means that the remote file is strictly newer.
# The local file can be safely overwritten.
logging.debug('Local file "%s" agrees with db record but remote item is different. Overwrite local.',
Expand Down Expand Up @@ -414,7 +420,7 @@ def _handle_local_file(self, item_name, item_record, item_stat, item_local_abspa
record_mtime_ts = datetime_to_timestamp(item_record.modified_time)
if item_stat.st_size == item_record.size_local and \
(diff_timestamps(item_stat.st_mtime, record_mtime_ts) == 0 or
item_record.sha1_hash and item_record.sha1_hash == sha1_value(item_local_abspath)):
item_record.sha1_hash and item_record.sha1_hash == sha1_value(item_local_abspath)):
logging.debug('Local file "%s" used to exist remotely but not found. Delete it.', item_local_abspath)
send2trash(item_local_abspath)
self.repo.delete_item(item_record.item_name, item_record.parent_path, False)
Expand Down Expand Up @@ -444,9 +450,4 @@ def _handle_local_item(self, item_name):
logging.error('Error occurred when accessing path "%s": %s.', item_local_abspath, e)


from . import create_folder, delete_item, download_file, upload_file
from .. import mkdir, fix_owner_and_timestamp
from ..od_api_helper import get_item_modified_datetime, item_request_call
from ..od_dateutils import datetime_to_timestamp, diff_timestamps
from ..od_hashutils import hash_match, sha1_value
from ..od_repo import ItemRecordType, ItemRecordStatus
from . import create_folder
12 changes: 6 additions & 6 deletions onedrived/od_watcher.py
Expand Up @@ -110,11 +110,11 @@ def ensure_remote_path_is_dir(self, repo, rel_path):
logging.info('Remote item "%s" in Drive %s is not a directory. Try renaming it to "%s".',
rel_path, repo.drive.id, new_name)
if not move_item.MoveItemTask(repo=repo, task_pool=self.task_pool,
parent_relpath=parent_relpath, item_name=item_name,
new_name=new_name, is_folder=False).handle():
parent_relpath=parent_relpath, item_name=item_name,
new_name=new_name, is_folder=False).handle():
if not delete_item.DeleteRemoteItemTask(repo=repo, task_pool=self.task_pool,
parent_relpath=parent_relpath,
item_name=item_name, is_folder=False).handle():
parent_relpath=parent_relpath,
item_name=item_name, is_folder=False).handle():
logging.warning('Failed to rename or delete remote item "%s" in Drive %s.',
rel_path, repo.drive.id)
return False
Expand All @@ -123,8 +123,8 @@ def ensure_remote_path_is_dir(self, repo, rel_path):
return False

if not create_folder.CreateFolderTask(repo=repo, task_pool=self.task_pool,
item_name=item_name, parent_relpath=parent_relpath,
upload_if_success=False, abort_if_local_gone=True).handle():
item_name=item_name, parent_relpath=parent_relpath,
upload_if_success=False, abort_if_local_gone=True).handle():
logging.critical('Failed to create remote directory "%s" on Drive %s.', rel_path, repo.drive.id)
return False
return True
Expand Down
4 changes: 2 additions & 2 deletions setup.cfg
@@ -1,3 +1,3 @@
[pycodestyle]
ignore = E401
max-line-length = 100
ignore = E401, E402
max-line-length = 120
6 changes: 0 additions & 6 deletions tests/__init__.py
@@ -1,6 +0,0 @@
try:
from unittest import mock
except ImportError:
# noinspection PyUnresolvedReferences
import mock

47 changes: 47 additions & 0 deletions tests/data/folder_child_item.json
@@ -0,0 +1,47 @@
{
"createdBy": {
"user": {
"id": "xybu_id",
"displayName": "Xiangyu Bu"
},
"application": {
"id": "4010c916",
"displayName": "onedrive-d"
}
},
"id": "xybu_id!339",
"fileSystemInfo": {
"createdDateTime": "2015-02-23T19:02:31.433Z",
"lastModifiedDateTime": "2015-02-23T19:02:31.433Z"
},
"eTag": "aNTNCRUFBRjA1NjgyNjg4MiEzMzkuMA",
"cTag": "aYzo1M0JFQUFGMDU2ODI2ODgyITMzOS4yNTY",
"createdDateTime": "2015-02-23T19:02:31.433Z",
"lastModifiedBy": {
"user": {
"id": "xybu_id",
"displayName": "Xiangyu Bu"
},
"application": {
"id": "4010c916",
"displayName": "onedrive-d"
}
},
"parentReference": {
"driveId": "xybu_id",
"id": "xybu_id!105",
"path": "/drive/root:/Public"
},
"webUrl": "https://onedrive.live.com/redir?resid=xybu_id!339",
"@content.downloadUrl": "https://what/no",
"size": 7632,
"file": {
"hashes": {
"sha1Hash": "84DCA3C4C2A464F21963F4EBAEFBD0042094D286",
"crc32Hash": "8A7A50C4"
},
"mimeType": "text/plain"
},
"name": "LICENSE",
"lastModifiedDateTime": "2015-02-23T19:02:31.433Z"
}
37 changes: 37 additions & 0 deletions tests/data/folder_item.json
@@ -0,0 +1,37 @@
{
"specialFolder": {
"name": "public"
},
"lastModifiedBy": {
"user": {
"displayName": "Xiangyu Bu",
"id": "xybu_id"
}
},
"folder": {
"childCount": 2
},
"id": "xybu_id!105",
"parentReference": {
"id": "xybu_id!103",
"driveId": "xybu_id",
"path": "/drive/root:"
},
"size": 7632,
"cTag": "adDo1M0JFQUFGMDU2ODI2ODgyITEwNS42MzU2MTIwODg0MjMwMDAwMDA",
"createdBy": {
"user": {
"displayName": "Xiangyu Bu",
"id": "xybu_id"
}
},
"name": "Public",
"lastModifiedDateTime": "2015-03-06T03:20:42.3Z",
"createdDateTime": "2013-11-25T03:54:33.86Z",
"eTag": "aNTNCRUFBRjA1NjgyNjg4MiExMDUuMQ",
"webUrl": "https://onedrive.live.com/redir?resid=xybu_id!105",
"fileSystemInfo": {
"lastModifiedDateTime": "2013-11-25T03:54:33.86Z",
"createdDateTime": "2013-11-25T03:54:33.86Z"
}
}
40 changes: 40 additions & 0 deletions tests/data/image_item.json
@@ -0,0 +1,40 @@
{
"@content.downloadUrl": "http://public-sn3302.files.1drv.com/y2pcT7OaUEExF7EHOlpTjCE55mIUoiX7H3sx1ff6I-nP35XUTBqZlnkh9FJhWb_pf9sZ7LEpEchvDznIbQig0hWBeidpwFkOqSKCwQylisarN6T0ecAeMvantizBUzM2PA1",
"createdDateTime": "2014-10-31T03:37:04.72Z",
"lastModifiedDateTime": "2014-05-06T07:08:09.33Z",
"cTag": "aYzpENDY0OEYwNkM5MUQ5RDNEITU0OTI3LjI1Ng",
"eTag": "aRDQ2NDhGMDZDOTFEOUQzRCE1NDkyNy4w",
"id": "D4648F06C91D9D3D!54927",
"createdBy": {
"user": {
"displayName": "xybu",
"id": "abc123"
}
},
"lastModifiedBy": {
"user": {
"displayName": "daron spektor",
"id": "d4648f06c91d9d3d"
}
},
"name": "BritishShorthair.jpg",
"description": "foobar!",
"size": 45,
"image": {
"height": 398,
"width": 273
},
"parentReference": {
"id": "53BEAAF056826882!103",
"driveId": "53beaaf056826882",
"path": "/drive/root:"
},
"file": {
"hashes": {
"crc32Hash": "omY5NA==",
"sha1Hash": "wmgPQ6jrSeMX7JP1XmstQEGM2fc="
},
"mimeType": "image/jpeg"
},
"webUrl": "http://foo/bar/baz"
}

0 comments on commit bb4ce19

Please sign in to comment.