From 11e909f20336027188d0c582de0ac10a3a6c0a18 Mon Sep 17 00:00:00 2001 From: Tania Mathern Date: Wed, 20 Nov 2024 15:13:34 -0800 Subject: [PATCH 1/7] Build script --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a741e292..80eb3628 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,12 @@ # Start from clean env: Delete `.venv`, then `python3 -m venv .venv` # Pre-requisite: Python virtual environment is active (source .venv/bin/activate) +release: + cargo build --release + build-python: rm -rf c2pa/c2pa python3 -m pip uninstall -y maturin python3 -m pip uninstall -y uniffi python3 -m pip install -r requirements.txt - maturin develop + maturin develop \ No newline at end of file From 0ede9b8e526c2ac7ce4679d676abfca323f7e3b0 Mon Sep 17 00:00:00 2001 From: Tania Mathern Date: Wed, 20 Nov 2024 15:23:59 -0800 Subject: [PATCH 2/7] Add getter --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d84e7b8b..e8a73508 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,10 @@ impl Reader { Err(Error::RwLock) } } + + pub fn get_reader(&self) -> &Reader { + self + } } pub struct Builder { From ca126d142aed26427bd454cd441e89043aee8050 Mon Sep 17 00:00:00 2001 From: Tania Mathern Date: Wed, 20 Nov 2024 15:55:11 -0800 Subject: [PATCH 3/7] Remove getter --- src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e8a73508..d84e7b8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,10 +111,6 @@ impl Reader { Err(Error::RwLock) } } - - pub fn get_reader(&self) -> &Reader { - self - } } pub struct Builder { From d02133b72f4c26e2593f900e418fa5042005d198 Mon Sep 17 00:00:00 2001 From: Tania Mathern Date: Wed, 20 Nov 2024 16:06:21 -0800 Subject: [PATCH 4/7] More exports --- c2pa/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/c2pa/__init__.py b/c2pa/__init__.py index 3a25a80c..13ac1aa2 100644 --- a/c2pa/__init__.py +++ b/c2pa/__init__.py @@ -13,6 +13,6 @@ from .c2pa_api import Reader, Builder, create_signer, create_remote_signer, sign_ps256 from .c2pa import Error, SigningAlg, CallbackSigner, sdk_version, version -from .c2pa.c2pa import _UniffiConverterTypeSigningAlg, _UniffiRustBuffer +from .c2pa.c2pa import _UniffiConverterTypeSigningAlg, _UniffiConverterTypeReader, _UniffiRustBuffer -__all__ = ['Reader', 'Builder', 'CallbackSigner', 'create_signer', 'sign_ps256', 'Error', 'SigningAlg', 'sdk_version', 'version', 'create_remote_signer', '_UniffiConverterTypeSigningAlg', '_UniffiRustBuffer'] +__all__ = ['Reader', 'Builder', 'CallbackSigner', 'create_signer', 'sign_ps256', 'Error', 'SigningAlg', 'sdk_version', 'version', 'create_remote_signer', '_UniffiConverterTypeSigningAlg', '_UniffiRustBuffer', '_UniffiConverterTypeReader'] From c75e8805f58ae75e46d3bb385d6c5817541d0e31 Mon Sep 17 00:00:00 2001 From: Tania Mathern Date: Wed, 20 Nov 2024 16:43:35 -0800 Subject: [PATCH 5/7] Add back getter --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d84e7b8b..2e2fbaf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,10 @@ impl Reader { Err(Error::RwLock) } } + + pub fn get_raw_reader(&self) -> &RwLock{ + &self.reader + } } pub struct Builder { From 7f9607f6e14e8f30eb572704ad5d0879e588662a Mon Sep 17 00:00:00 2001 From: Tania Mathern Date: Thu, 21 Nov 2024 09:31:49 -0800 Subject: [PATCH 6/7] Bump version --- Cargo.toml | 2 +- Makefile | 6 +- tests/test_api.py | 235 ++++++++++++++++++++------------------- tests/test_unit_tests.py | 3 +- 4 files changed, 128 insertions(+), 118 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fe27156d..5441e2c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "c2pa-python" -version = "0.6.1" +version = "0.6.2" edition = "2021" authors = ["Gavin Peacock bytes: - return sign_ps256(data, key) - - certs = open(data_dir + "ps256.pub", "rb").read() - # Create a local signer from a certificate pem file - signer = create_signer(sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") - - builder = Builder(manifest_def) - - builder.add_ingredient_file(ingredient_def, data_dir + "A.jpg") - - builder.add_resource_file("A.jpg", data_dir + "A.jpg") - - builder.to_archive(open("target/archive.zip", "wb")) - - builder = Builder.from_archive(open("target/archive.zip", "rb")) - - with tempfile.TemporaryDirectory() as output_dir: - output_path = output_dir + "out.jpg" - if os.path.exists(output_path): - os.remove(output_path) - c2pa_data = builder.sign_file(signer, data_dir + "A.jpg", output_dir + "out.jpg") - assert len(c2pa_data) > 0 - - reader = Reader.from_file(output_dir + "out.jpg") - print(reader.json()) - manifest_store = json.loads(reader.json()) - manifest = manifest_store["manifests"][manifest_store["active_manifest"]] - assert "python_test" in manifest["claim_generator"] - # check custom title and format - assert manifest["title"]== "My Title" - assert manifest,["format"] == "image/jpeg" - # There should be no validation status errors - assert manifest.get("validation_status") == None - assert manifest["ingredients"][0]["relationship"] == "parentOf" - assert manifest["ingredients"][0]["title"] == "A.jpg" - except Exception as e: - print("Failed to sign manifest store: " + str(e)) - exit(1) - -# Test signing the same source and destination file -def test_v2_sign_file_same(): - data_dir = "tests/fixtures/" - try: - key = open(data_dir + "ps256.pem", "rb").read() - def sign(data: bytes) -> bytes: - return sign_ps256(data, key) - - certs = open(data_dir + "ps256.pub", "rb").read() - # Create a local signer from a certificate pem file - signer = create_signer(sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") - - builder = Builder(manifest_def) - - builder.add_resource_file("A.jpg", data_dir + "A.jpg") - - with tempfile.TemporaryDirectory() as output_dir: - path = output_dir + "/A.jpg" - # Copy the file from data_dir to output_dir - shutil.copy(data_dir + "A.jpg", path) - c2pa_data = builder.sign_file(signer, path, path) - assert len(c2pa_data) > 0 - - reader = Reader.from_file(path) - manifest = reader.get_active_manifest() + def test_v2_read(self): + #example of reading a manifest store from a file + try: + reader = Reader.from_file("tests/fixtures/C.jpg") + manifest = reader.get_active_manifest() + assert manifest is not None + assert "make_test_images" in manifest["claim_generator"] + assert manifest["title"]== "C.jpg" + assert manifest["format"] == "image/jpeg" + # There should be no validation status errors + assert manifest.get("validation_status") == None + # read creative work assertion (author name) + assert getitem(manifest,("assertions",0,"label")) == "stds.schema-org.CreativeWork" + assert getitem(manifest,("assertions",0,"data","author",0,"name")) == "Adobe make_test" + # read Actions assertion + assert getitem(manifest,("assertions",1,"label")) == "c2pa.actions" + assert getitem(manifest,("assertions",1,"data","actions",0,"action")) == "c2pa.created" + # read signature info + assert getitem(manifest,("signature_info","issuer")) == "C2PA Test Signing Cert" + # read thumbnail data from file + assert getitem(manifest,("thumbnail","format")) == "image/jpeg" + # check the thumbnail data + uri = getitem(manifest,("thumbnail","identifier")) + reader.resource_to_file(uri, "target/thumbnail_read_v2.jpg") + + except Exception as e: + print("Failed to read manifest store: " + str(e)) + exit(1) + + def test_reader_from_file_no_store(self): + with pytest.raises(Error.ManifestNotFound) as err: + reader = Reader.from_file("tests/fixtures/A.jpg") + +class TestSignerr(unittest.TestCase): + def test_v2_sign(self): + # define a source folder for any assets we need to read + data_dir = "tests/fixtures/" + try: + key = open(data_dir + "ps256.pem", "rb").read() + def sign(data: bytes) -> bytes: + return sign_ps256(data, key) + + certs = open(data_dir + "ps256.pub", "rb").read() + # Create a local signer from a certificate pem file + signer = create_signer(sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") + + builder = Builder(manifest_def) + + builder.add_ingredient_file(ingredient_def, data_dir + "A.jpg") + + builder.add_resource_file("A.jpg", data_dir + "A.jpg") + + builder.to_archive(open("target/archive.zip", "wb")) + + builder = Builder.from_archive(open("target/archive.zip", "rb")) + + with tempfile.TemporaryDirectory() as output_dir: + output_path = output_dir + "out.jpg" + if os.path.exists(output_path): + os.remove(output_path) + c2pa_data = builder.sign_file(signer, data_dir + "A.jpg", output_dir + "out.jpg") + assert len(c2pa_data) > 0 + + reader = Reader.from_file(output_dir + "out.jpg") + print(reader.json()) + manifest_store = json.loads(reader.json()) + manifest = manifest_store["manifests"][manifest_store["active_manifest"]] + assert "python_test" in manifest["claim_generator"] # check custom title and format assert manifest["title"]== "My Title" - assert manifest["format"] == "image/jpeg" + assert manifest,["format"] == "image/jpeg" # There should be no validation status errors assert manifest.get("validation_status") == None - except Exception as e: - print("Failed to sign manifest store: " + str(e)) - #exit(1) + assert manifest["ingredients"][0]["relationship"] == "parentOf" + assert manifest["ingredients"][0]["title"] == "A.jpg" + except Exception as e: + print("Failed to sign manifest store: " + str(e)) + exit(1) + + # Test signing the same source and destination file + def test_v2_sign_file_same(self): + data_dir = "tests/fixtures/" + try: + key = open(data_dir + "ps256.pem", "rb").read() + def sign(data: bytes) -> bytes: + return sign_ps256(data, key) + + certs = open(data_dir + "ps256.pub", "rb").read() + # Create a local signer from a certificate pem file + signer = create_signer(sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") + + builder = Builder(manifest_def) + + builder.add_resource_file("A.jpg", data_dir + "A.jpg") + + with tempfile.TemporaryDirectory() as output_dir: + path = output_dir + "/A.jpg" + # Copy the file from data_dir to output_dir + shutil.copy(data_dir + "A.jpg", path) + c2pa_data = builder.sign_file(signer, path, path) + assert len(c2pa_data) > 0 + + reader = Reader.from_file(path) + manifest = reader.get_active_manifest() + + # check custom title and format + assert manifest["title"]== "My Title" + assert manifest["format"] == "image/jpeg" + # There should be no validation status errors + assert manifest.get("validation_status") == None + except Exception as e: + print("Failed to sign manifest store: " + str(e)) + #exit(1) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/test_unit_tests.py b/tests/test_unit_tests.py index fedbcf84..d10ab197 100644 --- a/tests/test_unit_tests.py +++ b/tests/test_unit_tests.py @@ -25,11 +25,10 @@ class TestC2paSdk(unittest.TestCase): def test_version(self): - self.assertIn("0.6.1", sdk_version()) + self.assertIn("0.6.2", sdk_version()) class TestReader(unittest.TestCase): - def test_stream_read(self): with open(testPath, "rb") as file: reader = Reader("image/jpeg",file) From 7a2dcdad3652378d259d5265c7ec946c6be0eb4a Mon Sep 17 00:00:00 2001 From: Tania Mathern Date: Thu, 21 Nov 2024 09:35:42 -0800 Subject: [PATCH 7/7] Readme notes --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4dce63eb..c64b5fcb 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,7 @@ auditwheel repair target/wheels/c2pa_python-0.4.0-py3-none-linux_aarch64.whl ### Testing -We use [PyTest](https://docs.pytest.org/) for testing. +We use [PyTest](https://docs.pytest.org/) and [unittest](https://docs.python.org/3/library/unittest.html) for testing. Run tests by following these steps: @@ -377,6 +377,23 @@ python3 tests/training.py deactivate ``` +#### Testing during bindings development + +While developing bindings locally, we recommend you rely on [unittest](https://docs.python.org/3/library/unittest.html), as [PyTest](https://docs.pytest.org/) can get confused by virtual environment redeployments (especially if you bump the version number). + +To run tests while developing bindings, you can run: + +```sh +make test +``` + +If you want to rebuild and test, run: + +```sh +make build-python +make test +``` + ## Release notes ### Version 0.5.2