Skip to content

Commit 80bc00e

Browse files
committed
fix: example
1 parent 05f0ca9 commit 80bc00e

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

tests/builder.test.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <gtest/gtest.h>
1515
#include <nlohmann/json.hpp>
1616
#include <algorithm>
17+
#include <fstream>
1718
#include <functional>
1819
#include <iostream>
1920
#include <memory>
@@ -5858,3 +5859,171 @@ TEST_F(BuilderTest, CreateIntentViaContext)
58585859
}
58595860
ASSERT_TRUE(found_created) << "Expected c2pa.created action in active manifest";
58605861
}
5862+
5863+
TEST_F(BuilderTest, ArchiveIngredientRoundTripAndReuse)
5864+
{
5865+
auto context = c2pa::Context();
5866+
auto source_path = c2pa_test::get_fixture_path("A.jpg");
5867+
5868+
// Shared instance_id links the parentOf ingredient to a c2pa.opened action.
5869+
// Thos will make the archive valid
5870+
std::string instance_id = "xmp:iid:archive-roundtrip-0001";
5871+
json manifest_json = {
5872+
{"claim_generator_info", json::array({{{"name", "c2pa-test"}, {"version", "1.0"}}})},
5873+
{"assertions", json::array({
5874+
{
5875+
{"label", "c2pa.actions"},
5876+
{"data", {
5877+
{"actions", json::array({
5878+
{
5879+
{"action", "c2pa.opened"},
5880+
{"parameters", {
5881+
{"ingredientIds", json::array({instance_id})}
5882+
}}
5883+
}
5884+
})}
5885+
}}
5886+
}
5887+
})}
5888+
};
5889+
5890+
// 1. Builder1: add parentOf ingredient linked to c2pa.opened, no signing.
5891+
auto builder1 = c2pa::Builder(context, manifest_json.dump());
5892+
json ingredient = {
5893+
{"title", "A.jpg"},
5894+
{"relationship", "parentOf"},
5895+
{"instance_id", instance_id}
5896+
};
5897+
builder1.add_ingredient(ingredient.dump(), source_path);
5898+
5899+
// 2. Archive to .c2pa file.
5900+
auto archive_path = get_temp_path("roundtrip-ingredient.c2pa");
5901+
ASSERT_NO_THROW(builder1.to_archive(archive_path));
5902+
ASSERT_TRUE(fs::exists(archive_path));
5903+
ASSERT_GT(fs::file_size(archive_path), 0u);
5904+
5905+
// 3. Read archive back; manifest must parse and carry the ingredient.
5906+
std::ifstream archive_in(archive_path, std::ios::binary);
5907+
c2pa::Reader archive_reader(context, "application/c2pa", archive_in);
5908+
std::string archive_json;
5909+
ASSERT_NO_THROW(archive_json = archive_reader.json());
5910+
5911+
auto parsed = json::parse(archive_json);
5912+
ASSERT_TRUE(parsed.contains("active_manifest"));
5913+
std::string active = parsed["active_manifest"];
5914+
auto ingredients = parsed["manifests"][active]["ingredients"];
5915+
ASSERT_EQ(ingredients.size(), 1u);
5916+
EXPECT_EQ(ingredients[0]["title"], "A.jpg");
5917+
EXPECT_EQ(ingredients[0]["relationship"], "parentOf");
5918+
5919+
// 4. Builder2: fresh manifest (training.json) + add the archive as an ingredient.
5920+
auto builder2_manifest = c2pa_test::read_text_file(c2pa_test::get_fixture_path("training.json"));
5921+
auto builder2 = c2pa::Builder(context, builder2_manifest);
5922+
json ingredient_override = {
5923+
{"title", "A.jpg"},
5924+
{"relationship", "componentOf"}
5925+
};
5926+
ASSERT_NO_THROW(builder2.add_ingredient(ingredient_override.dump(), archive_path));
5927+
5928+
// 5. Sign builder2 and validate signed output.
5929+
auto signer = c2pa_test::create_test_signer();
5930+
auto output_path = get_temp_path("archive_reused_output.jpg");
5931+
std::vector<unsigned char> manifest_data;
5932+
ASSERT_NO_THROW(manifest_data = builder2.sign(source_path, output_path, signer));
5933+
ASSERT_FALSE(manifest_data.empty());
5934+
5935+
auto out_reader = c2pa::Reader(context, output_path);
5936+
std::string out_json;
5937+
ASSERT_NO_THROW(out_json = out_reader.json());
5938+
auto out_parsed = json::parse(out_json);
5939+
std::string out_active = out_parsed["active_manifest"];
5940+
auto out_ingredients = out_parsed["manifests"][out_active]["ingredients"];
5941+
ASSERT_EQ(out_ingredients.size(), 1u);
5942+
EXPECT_EQ(out_ingredients[0]["title"], "A.jpg");
5943+
EXPECT_EQ(out_ingredients[0]["relationship"], "componentOf");
5944+
}
5945+
5946+
TEST_F(BuilderTest, ArchiveIngredientWithProvenanceRoundTripAndReuse)
5947+
{
5948+
auto context = c2pa::Context();
5949+
auto source_path = c2pa_test::get_fixture_path("A.jpg");
5950+
auto ingredient_path = c2pa_test::get_fixture_path("C.jpg");
5951+
5952+
// Shared instance_id links the parentOf ingredient to a c2pa.opened action.
5953+
std::string instance_id = "xmp:iid:archive-roundtrip-provenance-0001";
5954+
5955+
json manifest_json = {
5956+
{"claim_generator_info", json::array({{{"name", "c2pa-test"}, {"version", "1.0"}}})},
5957+
{"assertions", json::array({
5958+
{
5959+
{"label", "c2pa.actions"},
5960+
{"data", {
5961+
{"actions", json::array({
5962+
{
5963+
{"action", "c2pa.opened"},
5964+
{"parameters", {
5965+
{"ingredientIds", json::array({instance_id})}
5966+
}}
5967+
}
5968+
})}
5969+
}}
5970+
}
5971+
})}
5972+
};
5973+
5974+
// 1. Builder1: add parentOf ingredient (C.jpg is C2PA-signed, has provenance).
5975+
auto builder1 = c2pa::Builder(context, manifest_json.dump());
5976+
json ingredient = {
5977+
{"title", "C.jpg"},
5978+
{"relationship", "parentOf"},
5979+
{"instance_id", instance_id}
5980+
};
5981+
builder1.add_ingredient(ingredient.dump(), ingredient_path);
5982+
5983+
// 2. Archive to .c2pa file.
5984+
auto archive_path = get_temp_path("roundtrip-provenance-ingredient.c2pa");
5985+
ASSERT_NO_THROW(builder1.to_archive(archive_path));
5986+
ASSERT_TRUE(fs::exists(archive_path));
5987+
ASSERT_GT(fs::file_size(archive_path), 0u);
5988+
5989+
// 3. Read archive back; manifest must parse and carry the ingredient.
5990+
std::ifstream archive_in(archive_path, std::ios::binary);
5991+
c2pa::Reader archive_reader(context, "application/c2pa", archive_in);
5992+
std::string archive_json;
5993+
ASSERT_NO_THROW(archive_json = archive_reader.json());
5994+
std::cout << archive_json << std::endl;
5995+
5996+
auto parsed = json::parse(archive_json);
5997+
ASSERT_TRUE(parsed.contains("active_manifest"));
5998+
std::string active = parsed["active_manifest"];
5999+
auto ingredients = parsed["manifests"][active]["ingredients"];
6000+
ASSERT_EQ(ingredients.size(), 1u);
6001+
EXPECT_EQ(ingredients[0]["title"], "C.jpg");
6002+
EXPECT_EQ(ingredients[0]["relationship"], "parentOf");
6003+
6004+
// 4. Builder2: fresh manifest + add the archive as an ingredient.
6005+
auto builder2_manifest = c2pa_test::read_text_file(c2pa_test::get_fixture_path("training.json"));
6006+
auto builder2 = c2pa::Builder(context, builder2_manifest);
6007+
json ingredient_override = {
6008+
{"title", "C.jpg"},
6009+
{"relationship", "componentOf"}
6010+
};
6011+
ASSERT_NO_THROW(builder2.add_ingredient(ingredient_override.dump(), archive_path));
6012+
6013+
// 5. Sign builder2 and validate signed output.
6014+
auto signer = c2pa_test::create_test_signer();
6015+
auto output_path = get_temp_path("archive_provenance_reused_output.jpg");
6016+
std::vector<unsigned char> manifest_data;
6017+
ASSERT_NO_THROW(manifest_data = builder2.sign(source_path, output_path, signer));
6018+
ASSERT_FALSE(manifest_data.empty());
6019+
6020+
auto out_reader = c2pa::Reader(context, output_path);
6021+
std::string out_json;
6022+
ASSERT_NO_THROW(out_json = out_reader.json());
6023+
auto out_parsed = json::parse(out_json);
6024+
std::string out_active = out_parsed["active_manifest"];
6025+
auto out_ingredients = out_parsed["manifests"][out_active]["ingredients"];
6026+
ASSERT_EQ(out_ingredients.size(), 1u);
6027+
EXPECT_EQ(out_ingredients[0]["title"], "C.jpg");
6028+
EXPECT_EQ(out_ingredients[0]["relationship"], "componentOf");
6029+
}

0 commit comments

Comments
 (0)