Skip to content

Commit 9615099

Browse files
authored
Merge pull request #115 from contentauth/tran/test-coverage
fix: Test coverage
2 parents 4753686 + 8a3b7a2 commit 9615099

File tree

2 files changed

+121
-1
lines changed

2 files changed

+121
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ source .venv/bin/activate
7171

7272
# load project dependencies
7373
pip install -r requirements.txt
74+
pip install -r requirements-dev.txt
7475

7576
# download library artifacts for the current version you want, eg v0.55.0
7677
python scripts/download_artifacts.py c2pa-v0.55.0

tests/test_unit_tests.py

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import warnings
2121

2222
from c2pa import Builder, C2paError as Error, Reader, C2paSigningAlg as SigningAlg, C2paSignerInfo, Signer, sdk_version
23-
from c2pa.c2pa import Stream, read_ingredient_file, read_file, sign_file
23+
from c2pa.c2pa import Stream, read_ingredient_file, read_file, sign_file, load_settings
2424

2525
# Suppress deprecation warnings
2626
warnings.filterwarnings("ignore", category=DeprecationWarning)
@@ -54,6 +54,17 @@ def test_stream_read_and_parse(self):
5454
title = manifest_store["manifests"][manifest_store["active_manifest"]]["title"]
5555
self.assertEqual(title, "C.jpg")
5656

57+
def test_stream_read_string_stream(self):
58+
with Reader("image/jpeg", self.testPath) as reader:
59+
json_data = reader.json()
60+
self.assertIn("C.jpg", json_data)
61+
62+
def test_stream_read_string_stream_and_parse(self):
63+
with Reader("image/jpeg", self.testPath) as reader:
64+
manifest_store = json.loads(reader.json())
65+
title = manifest_store["manifests"][manifest_store["active_manifest"]]["title"]
66+
self.assertEqual(title, "C.jpg")
67+
5768
def test_reader_bad_format(self):
5869
with self.assertRaises(Error.NotSupported):
5970
with open(self.testPath, "rb") as file:
@@ -91,6 +102,13 @@ def test_reader_close_cleanup(self):
91102
self.assertIsNone(reader._own_stream)
92103
# Verify reader is marked as closed
93104
self.assertTrue(reader._closed)
105+
106+
def test_resource_to_stream_on_closed_reader(self):
107+
"""Test that resource_to_stream correctly raises error on closed."""
108+
reader = Reader("image/jpeg", self.testPath)
109+
reader.close()
110+
with self.assertRaises(Error):
111+
reader.resource_to_stream("", io.BytesIO(bytearray()))
94112

95113
def test_read_all_files(self):
96114
"""Test reading C2PA metadata from all files in the fixtures/files-for-reading-tests directory"""
@@ -198,6 +216,11 @@ def setUp(self):
198216
]
199217
}
200218

219+
def test_reserve_size_on_closed_signer(self):
220+
self.signer.close()
221+
with self.assertRaises(Error):
222+
self.signer.reserve_size()
223+
201224
def test_streams_sign(self):
202225
with open(self.testPath, "rb") as file:
203226
builder = Builder(self.manifestDefinition)
@@ -313,6 +336,17 @@ def test_builder_double_close(self):
313336
# Verify builder is closed
314337
with self.assertRaises(Error):
315338
builder.set_no_embed()
339+
340+
def test_builder_add_ingredient_on_closed_builder(self):
341+
"""Test that exception is raised when trying to add ingredient after close."""
342+
builder = Builder(self.manifestDefinition)
343+
344+
builder.close()
345+
346+
with self.assertRaises(Error):
347+
ingredient_json = '{"test": "ingredient"}'
348+
with open(self.testPath, 'rb') as f:
349+
builder.add_ingredient(ingredient_json, "image/jpeg", f)
316350

317351
def test_builder_add_ingredient(self):
318352
"""Test Builder class operations with a real file."""
@@ -395,6 +429,55 @@ def test_builder_sign_with_ingredient(self):
395429

396430
builder.close()
397431

432+
def test_builder_sign_with_duplicate_ingredient(self):
433+
"""Test Builder class operations with a real file."""
434+
# Test creating builder from JSON
435+
436+
builder = Builder.from_json(self.manifestDefinition)
437+
assert builder._builder is not None
438+
439+
# Test adding ingredient
440+
ingredient_json = '{"title": "Test Ingredient"}'
441+
with open(self.testPath3, 'rb') as f:
442+
builder.add_ingredient(ingredient_json, "image/jpeg", f)
443+
builder.add_ingredient(ingredient_json, "image/jpeg", f)
444+
builder.add_ingredient(ingredient_json, "image/jpeg", f)
445+
446+
with open(self.testPath2, "rb") as file:
447+
output = io.BytesIO(bytearray())
448+
builder.sign(self.signer, "image/jpeg", file, output)
449+
output.seek(0)
450+
reader = Reader("image/jpeg", output)
451+
json_data = reader.json()
452+
manifest_data = json.loads(json_data)
453+
454+
# Verify active manifest exists
455+
self.assertIn("active_manifest", manifest_data)
456+
active_manifest_id = manifest_data["active_manifest"]
457+
458+
# Verify active manifest object exists
459+
self.assertIn("manifests", manifest_data)
460+
self.assertIn(active_manifest_id, manifest_data["manifests"])
461+
active_manifest = manifest_data["manifests"][active_manifest_id]
462+
463+
# Verify ingredients array exists in active manifest
464+
self.assertIn("ingredients", active_manifest)
465+
self.assertIsInstance(active_manifest["ingredients"], list)
466+
self.assertTrue(len(active_manifest["ingredients"]) > 0)
467+
468+
# Verify the first ingredient's title matches what we set
469+
first_ingredient = active_manifest["ingredients"][0]
470+
self.assertEqual(first_ingredient["title"], "Test Ingredient")
471+
472+
# Verify subsequent labels are unique and have a double underscore with a monotonically inc. index
473+
second_ingredient = active_manifest["ingredients"][1]
474+
self.assertTrue(second_ingredient["label"].endswith("__1"))
475+
476+
third_ingredient = active_manifest["ingredients"][2]
477+
self.assertTrue(third_ingredient["label"].endswith("__2"))
478+
479+
builder.close()
480+
398481
def test_builder_sign_with_ingredient_from_stream(self):
399482
"""Test Builder class operations with a real file using stream for ingredient."""
400483
# Test creating builder from JSON
@@ -533,6 +616,37 @@ def test_builder_sign_with_multiple_ingredients_from_stream(self):
533616

534617
builder.close()
535618

619+
def test_builder_set_remote_url(self):
620+
"""Test setting the remote url of a builder."""
621+
builder = Builder.from_json(self.manifestDefinition)
622+
builder.set_remote_url("http://this_does_not_exist/foo.jpg")
623+
624+
with open(self.testPath2, "rb") as file:
625+
output = io.BytesIO(bytearray())
626+
builder.sign(self.signer, "image/jpeg", file, output)
627+
output.seek(0)
628+
d = output.read()
629+
self.assertIn(b'provenance="http://this_does_not_exist/foo.jpg"', d)
630+
631+
def test_builder_set_remote_url_no_embed(self):
632+
"""Test setting the remote url of a builder with no embed flag."""
633+
builder = Builder.from_json(self.manifestDefinition)
634+
load_settings(r'{"verify": { "remote_manifest_fetch": false} }')
635+
builder.set_no_embed()
636+
builder.set_remote_url("http://this_does_not_exist/foo.jpg")
637+
638+
with open(self.testPath2, "rb") as file:
639+
output = io.BytesIO(bytearray())
640+
builder.sign(self.signer, "image/jpeg", file, output)
641+
output.seek(0)
642+
with self.assertRaises(Error) as e:
643+
Reader("image/jpeg", output)
644+
645+
self.assertIn("http://this_does_not_exist/foo.jpg", e.exception.message)
646+
647+
# Return back to default settings
648+
load_settings(r'{"verify": { "remote_manifest_fetch": true} }')
649+
536650
class TestStream(unittest.TestCase):
537651
def setUp(self):
538652
# Create a temporary file for testing
@@ -692,6 +806,11 @@ def tearDown(self):
692806
import shutil
693807
shutil.rmtree(self.temp_data_dir)
694808

809+
def test_invalid_settings_str(self):
810+
"""Test loading a malformed settings string."""
811+
with self.assertRaises(Error):
812+
load_settings(r'{"verify": { "remote_manifest_fetch": false }')
813+
695814
def test_read_ingredient_file(self):
696815
"""Test reading a C2PA ingredient from a file."""
697816
# Test reading ingredient from file with data_dir

0 commit comments

Comments
 (0)