Skip to content

Commit e3ffd95

Browse files
committed
fix: Increase test coverage, fix accordingly 2
1 parent fca9a1c commit e3ffd95

File tree

2 files changed

+125
-4
lines changed

2 files changed

+125
-4
lines changed

src/c2pa/c2pa.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
'c2pa_builder_supported_mime_types',
4848
]
4949

50+
# TODO Bindings:
51+
# c2pa_reader_is_embedded
52+
# c2pa_reader_remote_url
53+
5054

5155
def _validate_library_exports(lib):
5256
"""Validate that all required functions are present in the loaded library.
@@ -579,7 +583,8 @@ def load_settings(settings: str, format: str = "json") -> None:
579583

580584
def read_ingredient_file(
581585
path: Union[str, Path], data_dir: Union[str, Path]) -> str:
582-
"""Read a C2PA ingredient from a file.
586+
"""Read a file as C2PA ingredient.
587+
This creates the JSON string that would be used as the ingredient JSON.
583588
584589
.. deprecated:: 0.11.0
585590
This function is deprecated and will be removed in a future version.
@@ -590,6 +595,13 @@ def read_ingredient_file(
590595
manifest_json = reader.json()
591596
```
592597
598+
To add ingredients to a manifest, please use the Builder class.
599+
Example:
600+
```
601+
with open(ingredient_file_path, 'rb') as f:
602+
builder.add_ingredient(ingredient_json, "image/jpeg", f)
603+
```
604+
593605
Args:
594606
path: Path to the file to read
595607
data_dir: Directory to write binary resources to
@@ -603,7 +615,9 @@ def read_ingredient_file(
603615
warnings.warn(
604616
"The read_ingredient_file function is deprecated and will be "
605617
"removed in a future version. Please use Reader(path).json() for "
606-
"reading C2PA metadata instead.",
618+
"reading C2PA metadata instead, or "
619+
"Builder.add_ingredient(json, format, stream) to add ingredients "
620+
"to a manifest.",
607621
DeprecationWarning,
608622
stacklevel=2,
609623
)

tests/test_unit_tests.py

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@
3333
PROJECT_PATH = os.getcwd()
3434
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixtures")
3535
DEFAULT_TEST_FILE_NAME = "C.jpg"
36+
INGREDIENT_TEST_FILE_NAME = "A.jpg"
3637
DEFAULT_TEST_FILE = os.path.join(FIXTURES_DIR, DEFAULT_TEST_FILE_NAME)
37-
INGREDIENT_TEST_FILE = os.path.join(FIXTURES_DIR, "A.jpg")
38+
INGREDIENT_TEST_FILE = os.path.join(FIXTURES_DIR, INGREDIENT_TEST_FILE_NAME)
3839
ALTERNATIVE_INGREDIENT_TEST_FILE = os.path.join(FIXTURES_DIR, "cloud.jpg")
3940

4041
class TestC2paSdk(unittest.TestCase):
@@ -816,7 +817,53 @@ def test_builder_sign_with_ingredient(self):
816817
assert builder._builder is not None
817818

818819
# Test adding ingredient
819-
ingredient_json = '{"title": "Test Ingredient"}'
820+
ingredient_json = '{ "title": "Test Ingredient" }'
821+
with open(self.testPath3, 'rb') as f:
822+
builder.add_ingredient(ingredient_json, "image/jpeg", f)
823+
824+
with open(self.testPath2, "rb") as file:
825+
output = io.BytesIO(bytearray())
826+
builder.sign(self.signer, "image/jpeg", file, output)
827+
output.seek(0)
828+
reader = Reader("image/jpeg", output)
829+
json_data = reader.json()
830+
manifest_data = json.loads(json_data)
831+
832+
# Verify active manifest exists
833+
self.assertIn("active_manifest", manifest_data)
834+
active_manifest_id = manifest_data["active_manifest"]
835+
836+
# Verify active manifest object exists
837+
self.assertIn("manifests", manifest_data)
838+
self.assertIn(active_manifest_id, manifest_data["manifests"])
839+
active_manifest = manifest_data["manifests"][active_manifest_id]
840+
841+
# Verify thumbnail for manifest is here
842+
self.assertIn("thumbnail", active_manifest)
843+
thumbnail_data = active_manifest["thumbnail"]
844+
self.assertIn("format", thumbnail_data)
845+
self.assertIn("identifier", thumbnail_data)
846+
847+
# Verify ingredients array exists in active manifest
848+
self.assertIn("ingredients", active_manifest)
849+
self.assertIsInstance(active_manifest["ingredients"], list)
850+
self.assertTrue(len(active_manifest["ingredients"]) > 0)
851+
852+
# Verify the first ingredient's title matches what we set
853+
first_ingredient = active_manifest["ingredients"][0]
854+
self.assertEqual(first_ingredient["title"], "Test Ingredient")
855+
856+
builder.close()
857+
858+
def test_builder_sign_with_setting_no_thumbnail_and_ingredient(self):
859+
builder = Builder.from_json(self.manifestDefinition)
860+
assert builder._builder is not None
861+
862+
# The following removes the manifest's thumbnail
863+
load_settings('{"builder": { "thumbnail": {"enabled": false}}}')
864+
865+
# Test adding ingredient
866+
ingredient_json = '{ "title": "Test Ingredient" }'
820867
with open(self.testPath3, 'rb') as f:
821868
builder.add_ingredient(ingredient_json, "image/jpeg", f)
822869

@@ -826,6 +873,7 @@ def test_builder_sign_with_ingredient(self):
826873
output.seek(0)
827874
reader = Reader("image/jpeg", output)
828875
json_data = reader.json()
876+
#print(json_data)
829877
manifest_data = json.loads(json_data)
830878

831879
# Verify active manifest exists
@@ -837,6 +885,9 @@ def test_builder_sign_with_ingredient(self):
837885
self.assertIn(active_manifest_id, manifest_data["manifests"])
838886
active_manifest = manifest_data["manifests"][active_manifest_id]
839887

888+
# There should be no thumbnail anymore here
889+
self.assertNotIn("thumbnail", active_manifest)
890+
840891
# Verify ingredients array exists in active manifest
841892
self.assertIn("ingredients", active_manifest)
842893
self.assertIsInstance(active_manifest["ingredients"], list)
@@ -848,6 +899,9 @@ def test_builder_sign_with_ingredient(self):
848899

849900
builder.close()
850901

902+
# Settings are global, so we reset to the default "true" here
903+
load_settings('{"builder": { "thumbnail": {"enabled": true}}}')
904+
851905
def test_builder_sign_with_duplicate_ingredient(self):
852906
builder = Builder.from_json(self.manifestDefinition)
853907
assert builder._builder is not None
@@ -1635,6 +1689,59 @@ def test_read_ingredient_file(self):
16351689
self.assertEqual(ingredient_data["format"], "image/jpeg")
16361690
self.assertIn("thumbnail", ingredient_data)
16371691

1692+
def test_read_ingredient_file_who_has_no_manifest(self):
1693+
"""Test reading a C2PA ingredient from a file."""
1694+
# Test reading ingredient from file with data_dir
1695+
temp_data_dir = os.path.join(self.data_dir, "temp_data")
1696+
os.makedirs(temp_data_dir, exist_ok=True)
1697+
1698+
load_settings('{"builder": { "thumbnail": {"enabled": false}}}')
1699+
1700+
ingredient_json_with_dir = read_ingredient_file(self.testPath2, temp_data_dir)
1701+
1702+
# Verify some fields
1703+
ingredient_data = json.loads(ingredient_json_with_dir)
1704+
self.assertEqual(ingredient_data["title"], INGREDIENT_TEST_FILE_NAME)
1705+
self.assertEqual(ingredient_data["format"], "image/jpeg")
1706+
self.assertIn("thumbnail", ingredient_data)
1707+
1708+
def test_compare_read_ingredient_file_with_builder_added_ingredient(self):
1709+
"""Test reading a C2PA ingredient from a file."""
1710+
# Test reading ingredient from file with data_dir
1711+
temp_data_dir = os.path.join(self.data_dir, "temp_data")
1712+
os.makedirs(temp_data_dir, exist_ok=True)
1713+
1714+
ingredient_json_with_dir = read_ingredient_file(self.testPath2, temp_data_dir)
1715+
1716+
# Ingredient fields from read_ingredient_file
1717+
ingredient_data = json.loads(ingredient_json_with_dir)
1718+
1719+
# Compare with ingredient added by Builder
1720+
builder = Builder.from_json(self.manifestDefinition)
1721+
# Only the title is needed (filename), since title not extracted or guessed from filename
1722+
ingredient_json = '{ "title" : "A.jpg" }'
1723+
with open(self.testPath2, 'rb') as f:
1724+
builder.add_ingredient(ingredient_json, "image/jpeg", f)
1725+
1726+
with open(self.testPath2, "rb") as file:
1727+
output = io.BytesIO(bytearray())
1728+
builder.sign(self.signer, "image/jpeg", file, output)
1729+
output.seek(0)
1730+
1731+
# Get ingredient fields from signed manifest
1732+
reader = Reader("image/jpeg", output)
1733+
json_data = reader.json()
1734+
manifest_data = json.loads(json_data)
1735+
active_manifest_id = manifest_data["active_manifest"]
1736+
active_manifest = manifest_data["manifests"][active_manifest_id]
1737+
only_ingredient = active_manifest["ingredients"][0]
1738+
1739+
self.assertEqual(ingredient_data["title"], only_ingredient["title"])
1740+
self.assertEqual(ingredient_data["format"], only_ingredient["format"])
1741+
self.assertEqual(ingredient_data["document_id"], only_ingredient["document_id"])
1742+
self.assertEqual(ingredient_data["instance_id"], only_ingredient["instance_id"])
1743+
self.assertEqual(ingredient_data["relationship"], only_ingredient["relationship"])
1744+
16381745
def test_read_file(self):
16391746
"""Test reading a C2PA ingredient from a file."""
16401747
temp_data_dir = os.path.join(self.data_dir, "temp_data")

0 commit comments

Comments
 (0)