From 962c010552b936e27f8f40e86f1737270ecfdc10 Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Sun, 30 Jun 2024 08:36:11 -0700 Subject: [PATCH] Updated tests to fix delay --- README.md | 13 +++++++++---- tests/conftest.py | 1 + tests/test_cli.py | 29 +++++++++++++++++++++-------- tests/test_datetime_utils.py | 1 + tests/test_mditem_attributes.py | 16 ++++++++++++++++ 5 files changed, 48 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d21907e..f832178 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Once you've installed osxmetadata with pip, to upgrade to the latest version: OSXMetaData uses setuptools, thus simply run: git clone https://github.com/RhetTbull/osxmetadata.git - cd osxmetadata + cd osxmetadata pip install poetry poetry install @@ -137,9 +137,14 @@ True The class attributes are handled dynamically which, unfortunately, means that IDEs like PyCharm and Visual Studio Code cannot provide tab-completion for them. +> [!NOTE] +> When writing or updating metadata with OSXMetaData, the OS will take some time to update the metadata on disk; in my testing, this can be as short as 100ms or as long as 3s. This means that if you read the metadata immediately after writing it, you may not see the updated metadata. If your use case requires the use of immediate read after write, you may need to implement a delay in your code to allow the OS time to update the metadata on disk. This appears to be an OS limitation and not something that can be controlled by osxmetadata. + +```pycon + ## Finder Tags -Unlike other attributes, which are mapped to native Python types appropriate for the source Objective C type, Finder tags (`_kMDItemUserTags` or `tags`) have two components: a name (str) and a color ID (unsigned int in range 0 to 7) representing a color tag in the Finder. Reading tags returns a list of `Tag` namedtuples and setting tags requires a list of `Tag` namedtuples. +Unlike other attributes, which are mapped to native Python types appropriate for the source Objective C type, Finder tags (`_kMDItemUserTags` or `tags`) have two components: a name (str) and a color ID (unsigned int in range 0 to 7) representing a color tag in the Finder. Reading tags returns a list of `Tag` namedtuples and setting tags requires a list of `Tag` namedtuples. ```pycon >>> from osxmetadata import * @@ -149,7 +154,7 @@ Unlike other attributes, which are mapped to native Python types appropriate for [Tag(name='Test', color=0), Tag(name='ToDo', color=6)] >>> md.get("_kMDItemUserTags") [Tag(name='Test', color=0), Tag(name='ToDo', color=6)] ->>> +>>> ``` Tag names (but not colors) can also be accessed through the [NSURLTagNamesKey](https://developer.apple.com/documentation/foundation/nsurltagnameskey) resource key and the label color ID is accessible through `NSURLLabelNumberKey`; the localized label color name is accessible through `NSURLLocalizedLabelKey` though these latter two resource keys only return a single color whereas a file may have more than one color tag. For most purposes, I recommend using the `tags` attribute as it is more convenient and provides access to both the name and color ID of the tag. @@ -224,7 +229,7 @@ Metadata attributes which return date/times such as `kMDItemDueDate` or `kMDItem >>> md.kMDItemDueDate datetime.datetime(2022, 10, 1, 0, 0) >>> md.kMDItemDownloadedDate = datetime.datetime(2022, 10, 1, tzinfo=datetime.timezone.utc) ->>> +>>> ``` ## Extended Attributes diff --git a/tests/conftest.py b/tests/conftest.py index ead6ed8..5e66ccf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,7 @@ SNOOZE_TIME = 0.5 if os.environ.get("GITHUB_ACTION") else 0.1 # Finder comments need more time to be written to disk FINDER_COMMENT_SNOOZE = 2.0 +LONG_SNOOZE = 3.0 # some tests need a longer snooze time def snooze(seconds: float = SNOOZE_TIME) -> None: diff --git a/tests/test_cli.py b/tests/test_cli.py index 6052f30..b9c11af 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,6 +2,7 @@ import datetime import glob +import json import os import pathlib @@ -13,7 +14,7 @@ from osxmetadata.__main__ import BACKUP_FILENAME, cli from osxmetadata.backup import load_backup_file -from .conftest import FINDER_COMMENT_SNOOZE, snooze +from .conftest import FINDER_COMMENT_SNOOZE, LONG_SNOOZE, snooze def parse_cli_output(output): @@ -264,8 +265,13 @@ def test_cli_remove(test_file): md = OSXMetaData(test_file.name) md.authors = ["John Doe", "Jane Doe"] md.tags = [Tag("test", 0)] + snooze() runner = CliRunner() + result = runner.invoke(cli, ["--list", "--json", test_file.name]) + data = json.loads(result.output) + assert sorted(data["kMDItemAuthors"]) == ["Jane Doe", "John Doe"] + result = runner.invoke( cli, [ @@ -275,15 +281,20 @@ def test_cli_remove(test_file): "--remove", "tags", "test,0", + "--verbose", test_file.name, ], ) - snooze() assert result.exit_code == 0 + assert "Removing John Doe from authors" in result.output + # for some reason this test fails without an additional delay + # for the removed metadata to be updated on disk + # without the additional delay, reading the metadata reads the previous value + snooze(LONG_SNOOZE) - md = OSXMetaData(test_file.name) - assert md.authors == ["Jane Doe"] - assert not md.tags + result = runner.invoke(cli, ["--list", "--json", test_file.name]) + data = json.loads(result.output) + assert data["kMDItemAuthors"] == ["Jane Doe"] def test_cli_mirror(test_file): @@ -495,7 +506,7 @@ def test_cli_backup_restore(test_dir): # wipe the data result = runner.invoke(cli, ["--wipe", test_file.as_posix()]) - snooze() + snooze(LONG_SNOOZE) md = OSXMetaData(test_file) assert not md.tags assert not md.authors @@ -503,6 +514,7 @@ def test_cli_backup_restore(test_dir): # restore the data result = runner.invoke(cli, ["--restore", test_file.as_posix()]) + snooze(LONG_SNOOZE) assert result.exit_code == 0 assert md.tags == [Tag("test", 0)] assert md.authors == ["John Doe", "Jane Doe"] @@ -556,6 +568,7 @@ def test_cli_order(test_dir): md.wherefroms = ["http://www.apple.com"] md.downloadeddate = [datetime.datetime(2019, 1, 1, 0, 0, 0)] md.findercomment = "Hello World" + snooze(LONG_SNOOZE) runner = CliRunner() @@ -564,7 +577,7 @@ def test_cli_order(test_dir): # wipe the data runner.invoke(cli, ["--wipe", test_file.as_posix()]) - snooze() + snooze(LONG_SNOOZE) # restore the data and check order of operations result = runner.invoke( @@ -602,7 +615,7 @@ def test_cli_order(test_dir): output = parse_cli_output(result.output) assert output["comment"] == "Hello World" - snooze() + snooze(LONG_SNOOZE) md = OSXMetaData(test_file) assert md.authors == ["John Smith", "Jane Smith"] assert md.findercomment == "Hello World" diff --git a/tests/test_datetime_utils.py b/tests/test_datetime_utils.py index 5874e41..19eecf8 100644 --- a/tests/test_datetime_utils.py +++ b/tests/test_datetime_utils.py @@ -1,4 +1,5 @@ """Test datetime_utils """ + import datetime import os from datetime import date, timezone diff --git a/tests/test_mditem_attributes.py b/tests/test_mditem_attributes.py index e8967ff..0ab7efe 100644 --- a/tests/test_mditem_attributes.py +++ b/tests/test_mditem_attributes.py @@ -173,3 +173,19 @@ def test_get_set_mditem_attribute_value(test_file): snooze() assert md.get_mditem_attribute_value("kMDItemComment") == "foo,bar" assert md.comment == "foo,bar" + + +def test_attribute_get_set(test_file): + """Test direct access get/set attribute values""" + + md = OSXMetaData(test_file.name) + assert not md.authors + md.authors = ["foo", "bar"] + snooze() + assert md.authors == ["foo", "bar"] + md.authors = ["bar"] + snooze() + assert md.authors == ["bar"] + md.set("authors", ["foo"]) + snooze() + assert md.authors == ["foo"]