diff --git a/Makefile b/Makefile index 36d6c5d6..02aafc13 100644 --- a/Makefile +++ b/Makefile @@ -80,11 +80,35 @@ test-coverage: clean cmake -S . -B build/coverage -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON $(CMAKE_OPTS) cmake --build build/coverage cd build/coverage && ctest --output-on-failure + @echo "" @echo "Generating coverage report..." - @lcov --capture --directory build/coverage --output-file build/coverage/coverage.info --ignore-errors mismatch,inconsistent,unsupported,format 2>&1 | grep -E "^(Capturing|geninfo|Found|Using|Recording|Writing|Scanning|Finished|Summary coverage rate:| (source files|lines|functions)|Filter|Message summary:| [0-9]+ (warning|error|ignore) message)" | grep -v "WARNING:" || true - @lcov --remove build/coverage/coverage.info '/usr/*' '*/googletest/*' '*/json-src/*' '*/c2pa_prebuilt-src/*' '*/tests/*' --output-file build/coverage/coverage_filtered.info --ignore-errors unused,mismatch,inconsistent,format 2>&1 | grep -E "^(Removing|Deleted|Writing|Summary coverage rate:| (source files|lines|functions))" | grep -v "Excluding" || true - @genhtml build/coverage/coverage_filtered.info --output-directory build/coverage/html --ignore-errors inconsistent,corrupt,unsupported,format,category 2>&1 | grep -E "^(Reading|Found|Generating|Processing|Overall coverage rate:| (source files|lines|functions))" || true - @echo "HTML report: build/coverage/html/index.html" + @lcov --capture --directory build/coverage --output-file build/coverage/coverage.info \ + --ignore-errors mismatch,inconsistent,unsupported,format 2>&1 \ + | grep -v "WARNING:" || { echo "Error: lcov capture failed"; exit 1; } + @lcov --remove build/coverage/coverage.info \ + '/usr/*' '*/googletest/*' '*/json-src/*' '*/c2pa_prebuilt-src/*' '*/tests/*' \ + --output-file build/coverage/coverage_filtered.info \ + --ignore-errors unused,mismatch,inconsistent,format 2>&1 \ + | grep -v "WARNING:" || { echo "Error: lcov filter failed"; exit 1; } + @echo "" + @echo "=== Coverage Summary ===" + @lcov --summary build/coverage/coverage_filtered.info \ + --ignore-errors inconsistent,format 2>&1 \ + | grep -E "(lines|functions|branches)" || true + @echo "========================" + @echo "" + @if command -v genhtml > /dev/null 2>&1; then \ + genhtml build/coverage/coverage_filtered.info --output-directory build/coverage/html \ + --ignore-errors inconsistent,corrupt,unsupported,format,category 2>&1 \ + | grep -v "WARNING:" || true; \ + if [ -f build/coverage/html/index.html ]; then \ + echo "HTML report: build/coverage/html/index.html"; \ + else \ + echo "Warning: HTML report was not generated (genhtml may have failed)"; \ + fi; \ + else \ + echo "Note: genhtml not found, skipping HTML report (install lcov for HTML reports)"; \ + fi # Demo targets demo: release diff --git a/docs/usage.md b/docs/usage.md index ac3a263e..dc93975c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -78,32 +78,53 @@ NOTE: If you don't specify a value for a property, then the SDK will use the def ### Creating a Context -The `Context` class provides several convenient factory methods for creating configured contexts: +The `Context` class manages C2PA SDK configuration. There are two ways to create a context: direct construction and the ContextBuilder. + +#### Direct construction ```cpp -// Create with default settings -auto context = c2pa::Context::create(); +// Default settings +c2pa::Context context; + +// From a Settings object +c2pa::Settings settings; +settings.set("builder.thumbnail.enabled", "true"); +c2pa::Context context(settings); -// Create from JSON configuration -auto context = c2pa::Context::from_json(R"({ +// From a JSON configuration string +c2pa::Context context(R"({ "builder": { "thumbnail": { "enabled": true } } })"); +``` + +#### ContextBuilder -// Create from Settings object for programmatic configuration +The builder pattern is useful when you need to layer multiple configuration sources: + +```cpp c2pa::Settings settings; settings.set("builder.thumbnail.enabled", "true"); -auto context = c2pa::Context::from_settings(settings); + +auto context = c2pa::Context::ContextBuilder() + .with_settings(settings) + .with_json(R"({"verify": {"verify_after_sign": true}})") + .create_context(); ``` -Once created, contexts can be passed to Builder and Reader constructors: +Note: the ContextBuilder is consumed after calling `create_context()`. For single-source configuration, prefer direct construction. + +#### Using a Context + +Contexts are passed by reference to Builder and Reader constructors. The context must remain valid for the lifetime of the Builder or Reader: ```cpp -auto builder = c2pa::Builder(context, manifest_json); -auto reader = c2pa::Reader(context, "image.jpg"); +c2pa::Context context; +c2pa::Builder builder(context, manifest_json); +c2pa::Reader reader(context, "image.jpg"); ``` ## Creating a Builder diff --git a/include/c2pa.hpp b/include/c2pa.hpp index 2801adb3..16cdfc82 100644 --- a/include/c2pa.hpp +++ b/include/c2pa.hpp @@ -223,17 +223,33 @@ namespace c2pa /// @brief C2PA context implementing IContextProvider. /// @details Context objects manage C2PA SDK configuration and state. - /// Use factory methods for simple creation: - /// - Context::create() for default settings - /// - Context::from_json() for JSON configuration - /// - Context::from_settings() for Settings objects + /// Contexts can be created via direct construction or the ContextBuilder: + /// + /// Direct construction: + /// @code + /// c2pa::Context ctx; // default + /// c2pa::Context ctx(settings); // from Settings + /// c2pa::Context ctx(json); // from JSON string + /// @endcode + /// + /// ContextBuilder (for multi-step configuration): + /// @code + /// auto ctx = c2pa::Context::ContextBuilder() + /// .with_settings(settings) + /// .with_json(json) + /// .create_context(); + /// @endcode + /// + /// When passed to Builder or Reader, the context is taken by reference + /// and must outlive the Builder/Reader instance. class C2PA_CPP_API Context : public IContextProvider { public: /// @brief ContextBuilder for creating customized Context instances. /// @details Provides a builder pattern for configuring contexts with multiple settings. /// Note: create_context() consumes the builder. - /// @note For most use cases, prefer the simpler factory methods: - /// Context::from_json() or Context::from_settings(). + /// @note For most use cases, prefer direct construction via the Context constructors. + /// The ContextBuilder is useful when you need to layer multiple configuration + /// sources (e.g. with_settings() followed by with_json()). class C2PA_CPP_API ContextBuilder { public: ContextBuilder(); @@ -264,37 +280,35 @@ namespace c2pa ContextBuilder& with_json(const std::string& json); /// @brief Create a Context from the current builder configuration. - /// @return Shared pointer to the new Context. + /// @return A new Context instance. /// @throws C2paException if context creation fails. /// @note This consumes the builder. After calling this, is_valid() returns false. - [[nodiscard]] std::shared_ptr create_context(); + [[nodiscard]] Context create_context(); private: C2paContextBuilder* context_builder; }; + // Direct construction /// @brief Create a Context with default settings. - /// @return Shared pointer to the new Context. /// @throws C2paException if context creation fails. - [[nodiscard]] static std::shared_ptr create(); + Context(); - /// @brief Create a Context from a Settings object. - /// @param settings Settings configuration. - /// @return Shared pointer to the new Context. + /// @brief Create a Context configured with a Settings object. + /// @param settings Settings configuration to apply. /// @throws C2paException if settings are invalid or context creation fails. - [[nodiscard]] static std::shared_ptr from_settings(const Settings& settings); + explicit Context(const Settings& settings); - /// @brief Create a Context from JSON configuration. + /// @brief Create a Context configured with a JSON string. /// @param json JSON configuration string. - /// @return Shared pointer to the new Context. /// @throws C2paException if JSON is invalid or context creation fails. - [[nodiscard]] static std::shared_ptr from_json(const std::string& json); + explicit Context(const std::string& json); - // Non-copyable, non-moveable + // Non-copyable, moveable Context(const Context&) = delete; Context& operator=(const Context&) = delete; - Context(Context&&) = delete; - Context& operator=(Context&&) = delete; + Context(Context&&) noexcept; + Context& operator=(Context&&) noexcept; ~Context() noexcept override; @@ -311,7 +325,9 @@ namespace c2pa /// external implementations that may have different lifecycle requirements. [[nodiscard]] bool has_context() const noexcept override; - /// @brief Internal constructor (use static factory methods instead). + /// @brief Internal constructor from raw FFI pointer (prefer public constructors). + /// @param ctx Raw C2paContext pointer — Context takes ownership. + /// @throws C2paException if ctx is nullptr. explicit Context(C2paContext* ctx); private: @@ -474,30 +490,32 @@ namespace c2pa C2paReader *c2pa_reader; std::unique_ptr owned_stream; // Owns file stream when created from path std::unique_ptr cpp_stream; // Wraps stream for C API; destroyed before owned_stream - std::shared_ptr reader_context; // Keeps context alive for this Reader instance + IContextProvider* reader_context; // Pointer to context (user must ensure it outlives Reader) public: /// @brief Create a Reader from a context and stream. - /// @param context Context provider to use for this reader. + /// @param context Context provider to use for this reader (must outlive Reader). /// @param format The mime format of the stream. /// @param stream The input stream to read from. - /// @throws C2pa::C2paException if context is null, context->has_context() returns false, + /// @throws C2pa::C2paException if context->has_context() returns false, /// or for other errors encountered by the C2PA library. - /// @note The constructor validates both that the context pointer is non-null AND that - /// has_context() returns true before using c_context(). This ensures external - /// IContextProvider implementations are in a valid state. - Reader(std::shared_ptr context, const std::string &format, std::istream &stream); + /// @note The constructor validates that has_context() returns true before using + /// c_context(). This ensures external IContextProvider implementations are + /// in a valid state. + /// @note The context must remain valid for the lifetime of this Reader. + Reader(IContextProvider& context, const std::string &format, std::istream &stream); /// @brief Create a Reader from a context and file path. - /// @param context Context provider to use for this reader. + /// @param context Context provider to use for this reader (must outlive Reader). /// @param source_path the path to the file to read. - /// @throws C2pa::C2paException if context is null, context->has_context() returns false, + /// @throws C2pa::C2paException if context->has_context() returns false, /// or for other errors encountered by the C2PA library. - /// @note The constructor validates both that the context pointer is non-null AND that - /// has_context() returns true before using c_context(). This ensures external - /// IContextProvider implementations are in a valid state. + /// @note The constructor validates that has_context() returns true before using + /// c_context(). This ensures external IContextProvider implementations are + /// in a valid state. + /// @note The context must remain valid for the lifetime of this Reader. /// @note Prefer using the streaming APIs if possible - Reader(std::shared_ptr context, const std::filesystem::path &source_path); + Reader(IContextProvider& context, const std::filesystem::path &source_path); /// @brief Create a Reader from a stream (will use global settings if any loaded). /// @details The validation_status field in the json contains validation results. @@ -525,8 +543,9 @@ namespace c2pa : c2pa_reader(other.c2pa_reader), owned_stream(std::move(other.owned_stream)), cpp_stream(std::move(other.cpp_stream)), - reader_context(std::move(other.reader_context)) { + reader_context(other.reader_context) { other.c2pa_reader = nullptr; + other.reader_context = nullptr; } Reader& operator=(Reader&& other) noexcept { if (this != &other) { @@ -536,8 +555,9 @@ namespace c2pa c2pa_reader = other.c2pa_reader; owned_stream = std::move(other.owned_stream); cpp_stream = std::move(other.cpp_stream); - reader_context = std::move(other.reader_context); + reader_context = other.reader_context; other.c2pa_reader = nullptr; + other.reader_context = nullptr; } return *this; } @@ -545,8 +565,8 @@ namespace c2pa ~Reader(); /// @brief Get the context associated with this Reader. - /// @return Shared pointer to the context, or nullptr if using legacy/context-free API. - [[nodiscard]] inline std::shared_ptr context() const noexcept { + /// @return Pointer to the context, or nullptr if using legacy/context-free API. + [[nodiscard]] inline IContextProvider* context() const noexcept { return reader_context; } @@ -661,27 +681,29 @@ namespace c2pa { private: C2paBuilder *builder; - std::shared_ptr builder_context; // Keeps context alive for this builder + IContextProvider* builder_context; // Pointer to context (user must ensure it outlives Builder) public: /// @brief Create a Builder from a context with an empty manifest. - /// @param context Context provider to use for this builder. - /// @throws C2pa::C2paException if context is null, context->has_context() returns false, + /// @param context Context provider to use for this builder (must outlive Builder). + /// @throws C2pa::C2paException if context->has_context() returns false, /// or for other errors encountered by the C2PA library. - /// @note The constructor validates both that the context pointer is non-null AND that - /// has_context() returns true before using c_context(). This ensures external - /// IContextProvider implementations are in a valid state. - explicit Builder(std::shared_ptr context); + /// @note The constructor validates that has_context() returns true before using + /// c_context(). This ensures external IContextProvider implementations are + /// in a valid state. + /// @note The context must remain valid for the lifetime of this Builder. + explicit Builder(IContextProvider& context); /// @brief Create a Builder from a context and manifest JSON string. - /// @param context Context provider to use for this builder. + /// @param context Context provider to use for this builder (must outlive Builder). /// @param manifest_json The manifest JSON string. - /// @throws C2pa::C2paException if context is null, context->has_context() returns false, + /// @throws C2pa::C2paException if context->has_context() returns false, /// or for other errors encountered by the C2PA library. - /// @note The constructor validates both that the context pointer is non-null AND that - /// has_context() returns true before using c_context(). This ensures external - /// IContextProvider implementations are in a valid state. - Builder(std::shared_ptr context, const std::string &manifest_json); + /// @note The constructor validates that has_context() returns true before using + /// c_context(). This ensures external IContextProvider implementations are + /// in a valid state. + /// @note The context must remain valid for the lifetime of this Builder. + Builder(IContextProvider& context, const std::string &manifest_json); /// @brief Create a Builder from a manifest JSON std::string (will use global settings if any loaded). /// @param manifest_json The manifest JSON string. @@ -695,16 +717,18 @@ namespace c2pa Builder& operator=(const Builder&) = delete; // Move semantics - Builder(Builder&& other) noexcept : builder(other.builder), builder_context(std::move(other.builder_context)) { + Builder(Builder&& other) noexcept : builder(other.builder), builder_context(other.builder_context) { other.builder = nullptr; + other.builder_context = nullptr; } Builder& operator=(Builder&& other) noexcept { if (this != &other) { if (builder != nullptr) c2pa_free(builder); builder = other.builder; - builder_context = std::move(other.builder_context); + builder_context = other.builder_context; other.builder = nullptr; + other.builder_context = nullptr; } return *this; } @@ -712,8 +736,8 @@ namespace c2pa ~Builder(); /// @brief Get the context associated with this Builder. - /// @return Shared pointer to the context, or nullptr if using legacy/context-free API. - [[nodiscard]] inline std::shared_ptr context() const noexcept { + /// @return Pointer to the context, or nullptr if using legacy/context-free API. + [[nodiscard]] inline IContextProvider* context() const noexcept { return builder_context; } @@ -771,12 +795,14 @@ namespace c2pa void add_ingredient(const std::string &ingredient_json, const std::filesystem::path &source_path); /// @brief Add an archive as an ingredient to the builder. + /// This method is a wrapper for add_ingredient(ingredient_json, C2paMimeType::BinaryArchive, archive); /// @param ingredient_json Any fields of the ingredient you want to define (e.g. title, relationship). /// @param archive The input stream to read the archive from. /// @throws C2pa::C2paException for errors encountered by the C2PA library. void from_ingredient_archive(const std::string &ingredient_json, std::istream &archive); /// @brief Add an archive (working store) as an ingredient to the builder. + /// This method is a wrapper for add_ingredient(ingredient_json, C2paMimeType::BinaryArchive, archive); /// @param ingredient_json Any fields of the ingredient you want to define (e.g. title, relationship). /// @param archive_path The path to the archive file. /// @throws C2pa::C2paException for errors encountered by the C2PA library. @@ -816,7 +842,7 @@ namespace c2pa /// @note Prefer using the streaming APIs if possible std::vector sign(const std::filesystem::path &source_path, const std::filesystem::path &dest_path, Signer &signer); - /// @brief Create a Builder from an archive. + /// @brief Create a Builder from an archived Builder. /// @param archive The input stream to read the archive from. /// @throws C2pa::C2paException for errors encountered by the C2PA library. static Builder from_archive(std::istream &archive); @@ -827,12 +853,13 @@ namespace c2pa /// @note Prefer using the streaming APIs if possible static Builder from_archive(const std::filesystem::path &archive_path); - /// @brief Load an archive into this builder, replacing its current manifest definition. + /// @brief Load an archive into this builder, replacing its current definition + /// with the reloaded archived builder state. /// @param archive The input stream to read the archive from. /// @return Reference to this builder for method chaining. /// @throws C2pa::C2paException for errors encountered by the C2PA library. /// @note This allows setting a context before loading the archive, preserving context settings. - Builder& load_archive(std::istream &archive); + Builder& with_archive(std::istream &archive); /// @brief Write the builder to an archive stream. /// @param dest The output stream to write the archive to. diff --git a/src/c2pa.cpp b/src/c2pa.cpp index cbd848fb..b853d3fb 100644 --- a/src/c2pa.cpp +++ b/src/c2pa.cpp @@ -375,29 +375,13 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 } } - Context::~Context() noexcept { - if (context) { - c2pa_free(context); - } - } - - C2paContext* Context::c_context() const noexcept { - return context; - } - - bool Context::has_context() const noexcept { - return context != nullptr; - } - - std::shared_ptr Context::create() { - auto ctx = c2pa_context_new(); - if (!ctx) { + Context::Context() : context(c2pa_context_new()) { + if (!context) { throw C2paException("Failed to create context"); } - return std::make_shared(ctx); } - std::shared_ptr Context::from_settings(const Settings& settings) { + Context::Context(const Settings& settings) : context(nullptr) { auto builder = c2pa_context_builder_new(); if (!builder) { throw C2paException("Failed to create context builder"); @@ -406,15 +390,42 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 c2pa_free(builder); throw C2paException(); } - C2paContext* ctx = c2pa_context_builder_build(builder); - if (!ctx) { + context = c2pa_context_builder_build(builder); + if (!context) { throw C2paException("Failed to build context"); } - return std::make_shared(ctx); } - std::shared_ptr Context::from_json(const std::string& json) { - return from_settings(Settings(json, "json")); + Context::Context(Context&& other) noexcept : context(other.context) { + other.context = nullptr; + } + + Context& Context::operator=(Context&& other) noexcept { + if (this != &other) { + if (context) { + c2pa_free(context); + } + context = other.context; + other.context = nullptr; + } + return *this; + } + + Context::~Context() noexcept { + if (context) { + c2pa_free(context); + } + } + + C2paContext* Context::c_context() const noexcept { + return context; + } + + bool Context::has_context() const noexcept { + return context != nullptr; + } + + Context::Context(const std::string& json) : Context(Settings(json, "json")) { } // Context::ContextBuilder @@ -466,7 +477,7 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 return with_settings(Settings(json, "json")); } - std::shared_ptr Context::ContextBuilder::create_context() { + Context Context::ContextBuilder::create_context() { if (!is_valid()) { throw C2paException("ContextBuilder is invalid (moved from)"); } @@ -480,7 +491,7 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 // Builder is consumed by the C API context_builder = nullptr; - return std::make_shared(ctx); + return Context(ctx); } /// Returns the version of the C2PA library. @@ -658,10 +669,10 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 /// Reader class for reading manifests - Reader::Reader(std::shared_ptr context, const std::string &format, std::istream &stream) - : c2pa_reader(nullptr), reader_context(std::move(context)) + Reader::Reader(IContextProvider& context, const std::string &format, std::istream &stream) + : c2pa_reader(nullptr), reader_context(&context) { - if (!reader_context || !reader_context->has_context()) { + if (!reader_context->has_context()) { throw C2paException("Invalid context provider"); } @@ -682,10 +693,10 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 c2pa_reader = updated; } - Reader::Reader(std::shared_ptr context, const std::filesystem::path &source_path) - : c2pa_reader(nullptr), reader_context(std::move(context)) + Reader::Reader(IContextProvider& context, const std::filesystem::path &source_path) + : c2pa_reader(nullptr), reader_context(&context) { - if (!reader_context || !reader_context->has_context()) { + if (!reader_context->has_context()) { throw C2paException("Invalid context provider"); } @@ -834,10 +845,10 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 /// @brief Builder class for creating a manifest implementation. - Builder::Builder(std::shared_ptr context) - : builder(nullptr), builder_context(std::move(context)) + Builder::Builder(IContextProvider& context) + : builder(nullptr), builder_context(&context) { - if (!builder_context || !builder_context->has_context()) { + if (!builder_context->has_context()) { throw C2paException("Invalid context provider"); } @@ -847,10 +858,10 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 } } - Builder::Builder(std::shared_ptr context, const std::string &manifest_json) - : builder(nullptr), builder_context(std::move(context)) + Builder::Builder(IContextProvider& context, const std::string &manifest_json) + : builder(nullptr), builder_context(&context) { - if (!builder_context || !builder_context->has_context()) { + if (!builder_context->has_context()) { throw C2paException("Invalid context provider"); } @@ -1068,7 +1079,7 @@ inline std::vector to_byte_vector(const unsigned char* data, int6 /// @param archive The input stream to read the archive from. /// @return Reference to this builder for method chaining. /// @throws C2pa::C2paException for errors encountered by the C2PA library. - Builder& Builder::load_archive(std::istream &archive) + Builder& Builder::with_archive(std::istream &archive) { CppIStream c_archive(archive); diff --git a/tests/builder.test.cpp b/tests/builder.test.cpp index 8192783f..10804be5 100644 --- a/tests/builder.test.cpp +++ b/tests/builder.test.cpp @@ -218,13 +218,13 @@ TEST(BuilderErrorHandling, MalformedJsonManifestReturnsError) TEST(BuilderErrorHandling, EmptyManifestJsonReturnsErrorWithContext) { - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); EXPECT_THROW(c2pa::Builder(context, ""), c2pa::C2paException); } TEST(BuilderErrorHandling, MalformedJsonManifestReturnsErrorWithContext) { - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); EXPECT_THROW(c2pa::Builder(context, "{ invalid json"), c2pa::C2paException); } @@ -247,7 +247,7 @@ TEST(BuilderErrorHandling, JsonErrorsBehaveSameWithAndWithoutContext) << "Without context, should throw for: " << bad_input; // With context - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); EXPECT_THROW({ c2pa::Builder builder(ctx, bad_input); }, c2pa::C2paException) @@ -265,7 +265,7 @@ TEST(BuilderErrorHandling, ValidJsonWorksWithAndWithoutContext) }); // With context - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); EXPECT_NO_THROW({ c2pa::Builder builder(ctx, valid_json); }); @@ -284,7 +284,7 @@ TEST(BuilderErrorHandling, FailedConstructionWithAndWithoutContext) // With context try { - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); c2pa::Builder(ctx, ""); FAIL() << "Should have thrown"; } catch (const c2pa::C2paException&) { @@ -306,7 +306,7 @@ TEST(BuilderErrorHandling, ErrorMessagesWithAndWithoutContext) // With context try { - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); c2pa::Builder(ctx, ""); FAIL() << "Should have thrown"; } catch (const c2pa::C2paException& e) { @@ -425,7 +425,7 @@ TEST_F(BuilderTest, AddAnActionAndSignUsingContext) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // Create a Context and pass it to the Builder - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest); // Add an action to the builder @@ -638,7 +638,7 @@ TEST_F(BuilderTest, AddMultipleActionsAndSignUsingContext) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // Create a Context and pass it to the Builder - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest); // Add multiple actions to the builder @@ -830,7 +830,7 @@ TEST_F(BuilderTest, SignImageFileNoThumbnailAutoGen) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // Test 1: Create context with specific settings via JSON - auto context = c2pa::Context::from_json("{\"builder\": { \"thumbnail\": {\"enabled\": false}}}"); + auto context = c2pa::Context("{\"builder\": { \"thumbnail\": {\"enabled\": false}}}"); // Create builder using context containing settings auto builder_with_context = c2pa::Builder(context, manifest); @@ -892,7 +892,7 @@ TEST_F(BuilderTest, SignImageThumbnailSettingsFileJson) // Create context with specific settings via JSON, by loading the JSON file with the settings fs::path settings_path = current_dir / "../tests/fixtures/settings/test_settings_no_thumbnail.json"; auto settings_json = c2pa_test::read_text_file(settings_path); - auto context = c2pa::Context::from_json(settings_json); + auto context = c2pa::Context(settings_json); // Create builder using context containing settings (does not generate thumbnails) auto builder_no_thumbnail = c2pa::Builder(context, manifest); @@ -909,7 +909,7 @@ TEST_F(BuilderTest, SignImageThumbnailSettingsFileJson) // Now, create builder with another context (settings generate a thumbnail) fs::path settings_path2 = current_dir / "../tests/fixtures/settings/test_settings_with_thumbnail.json"; auto settings_json2 = c2pa_test::read_text_file(settings_path2); - auto context2 = c2pa::Context::from_json(settings_json2); + auto context2 = c2pa::Context(settings_json2); auto builder_with_thumbnail = c2pa::Builder(context2, manifest); std::vector manifest_data_with_thumbnail; @@ -1088,7 +1088,7 @@ TEST_F(BuilderTest, SignImageFileWithResourceUsingContext) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // Create a Context and pass it to the Builder - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest); // add a resource: a thumbnail builder.add_resource("thumbnail", image_path); @@ -1271,7 +1271,7 @@ TEST_F(BuilderTest, SignVideoFileWithMultipleIngredientsUsingContext) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // create the builder with context - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest); // add the ingredients for the video @@ -1440,7 +1440,7 @@ TEST_F(BuilderTest, SignImageStream) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // Create a default context (no custom settings) - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); // Create builder with context auto builder = c2pa::Builder(context, manifest); @@ -1492,7 +1492,7 @@ TEST_F(BuilderTest, SignImageStreamBuilderReaderDifferentContext) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // Create a default context (no custom settings, defaults to SDK default settings) - auto write_context = c2pa::Context::create(); + auto write_context = c2pa::Context(); // Create builder with context auto builder = c2pa::Builder(write_context, manifest); @@ -1522,7 +1522,7 @@ TEST_F(BuilderTest, SignImageStreamBuilderReaderDifferentContext) // (Here for this test we use a demo empty context again) // Create a default context (no custom settings) - auto read_context = c2pa::Context::create(); + auto read_context = c2pa::Context(); auto reader = c2pa::Reader(read_context, "image/jpeg", dest); std::string json; @@ -1692,7 +1692,7 @@ TEST_F(BuilderTest, SignDataHashedEmbeddedUsingContext) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // Create a Context and pass it to the Builder - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest); auto placeholder = builder.data_hashed_placeholder(signer.reserve_size(), "image/jpeg"); @@ -1777,7 +1777,7 @@ TEST_F(BuilderTest, SignDataHashedEmbeddedWithAssetUsingContext) c2pa::Signer signer = c2pa::Signer("Es256", certs, p_key, "http://timestamp.digicert.com"); // Create a Context and pass it to the Builder - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest); auto placeholder = builder.data_hashed_placeholder(signer.reserve_size(), "image/jpeg"); @@ -2190,7 +2190,7 @@ TEST_F(BuilderTest, LinkIngredientsAndSignUsingContext) } // Create a Context and pass it to the Builder - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json.dump()); // add an ingredient @@ -2457,7 +2457,7 @@ TEST_F(BuilderTest, AddIngredientToBuilderUsingBasePathWithManifestContainingPla fs::path temp_dir = get_temp_dir("ingredient_placed_context"); // Create context with auto_placed_action disabled via JSON settings - auto context = c2pa::Context::from_json("{\"builder\": { \"actions\": {\"auto_placed_action\": {\"enabled\": false}}}}"); + auto context = c2pa::Context("{\"builder\": { \"actions\": {\"auto_placed_action\": {\"enabled\": false}}}}"); // Get the needed JSON for the ingredient std::string result; @@ -2611,12 +2611,12 @@ TEST_F(BuilderTest, MultipleBuildersDifferentThumbnailSettingsInterleaved) // Create one context with specific settings via JSON, by loading the JSON file with the settings fs::path settings_path = current_dir / "../tests/fixtures/settings/test_settings_no_thumbnail.json"; auto settings_json = c2pa_test::read_text_file(settings_path); - auto context_without_thumbnails = c2pa::Context::from_json(settings_json); + auto context_without_thumbnails = c2pa::Context(settings_json); // Now, create anothetrcontext, that sets thumbnails to be generated fs::path settings_path2 = current_dir / "../tests/fixtures/settings/test_settings_with_thumbnail.json"; auto settings_json2 = c2pa_test::read_text_file(settings_path2); - auto context_with_thumbnails = c2pa::Context::from_json(settings_json2); + auto context_with_thumbnails = c2pa::Context(settings_json2); // Create builder using context containing settings that does not generate thumbnails auto builder_no_thumbnail = c2pa::Builder(context_without_thumbnails, manifest); @@ -2678,12 +2678,12 @@ TEST_F(BuilderTest, MultipleBuildersDifferentThumbnailSettingsInterleaved2) // Create one context with specific settings via JSON, by loading the JSON file with the settings fs::path settings_path = current_dir / "../tests/fixtures/settings/test_settings_no_thumbnail.json"; auto settings_json = c2pa_test::read_text_file(settings_path); - auto context_without_thumbnails = c2pa::Context::from_json(settings_json); + auto context_without_thumbnails = c2pa::Context(settings_json); // Now, create another context, that sets thumbnails to be generated fs::path settings_path2 = current_dir / "../tests/fixtures/settings/test_settings_with_thumbnail.json"; auto settings_json2 = c2pa_test::read_text_file(settings_path2); - auto context_with_thumbnails = c2pa::Context::from_json(settings_json2); + auto context_with_thumbnails = c2pa::Context(settings_json2); // Create builder using context containing settings that does generate thumbnails auto builder_with_thumbnail = c2pa::Builder(context_with_thumbnails, manifest); @@ -2794,7 +2794,7 @@ TEST_F(BuilderTest, TrustHandling) // If not, we won't be able to read the manifest as trusted! fs::path settings_without_trust_path = current_dir / "../tests/fixtures/settings/test_settings_no_thumbnail.json"; auto settings_without_trust = c2pa_test::read_text_file(settings_without_trust_path); - auto no_trust_context = c2pa::Context::from_json(settings_without_trust); + auto no_trust_context = c2pa::Context(settings_without_trust); auto reader3 = c2pa::Reader(no_trust_context, output_path); std::string read_json_manifest3; @@ -2882,7 +2882,7 @@ TEST_F(BuilderTest, SignWithIStreamAndIOStream_RoundTrip) TEST_F(BuilderTest, ArchiveRoundTrip) { auto manifest = c2pa_test::read_text_file(c2pa_test::get_fixture_path("training.json")); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder1 = c2pa::Builder(context, manifest); // Export to archive @@ -2916,7 +2916,7 @@ TEST_F(BuilderTest, ArchiveRoundTripSettingsBehavior) // Create context with settings that disable thumbnail generation auto settings_json = c2pa_test::read_text_file(c2pa_test::get_fixture_path("settings/test_settings_no_thumbnail.json")); - auto context_no_thumbnail = c2pa::Context::from_json(settings_json); + auto context_no_thumbnail = c2pa::Context(settings_json); // Verify the setting works when set on builder with context (baseline) auto builder_direct = c2pa::Builder(context_no_thumbnail, manifest); @@ -2929,7 +2929,8 @@ TEST_F(BuilderTest, ArchiveRoundTripSettingsBehavior) }); // Verify no thumbnail in direct sign dest_direct.seekg(0); - auto reader_direct = c2pa::Reader(c2pa::Context::create(), "image/jpeg", dest_direct); + auto ctx_reader_direct = c2pa::Context(); + auto reader_direct = c2pa::Reader(ctx_reader_direct, "image/jpeg", dest_direct); auto json_direct = json::parse(reader_direct.json()); std::string active_direct = json_direct["active_manifest"]; EXPECT_FALSE(json_direct["manifests"][active_direct].contains("thumbnail")) @@ -2955,7 +2956,8 @@ TEST_F(BuilderTest, ArchiveRoundTripSettingsBehavior) }); // Verify manifest structure is preserved dest_archive.seekg(0); - auto reader_archive = c2pa::Reader(c2pa::Context::create(), "image/jpeg", dest_archive); + auto ctx_reader_archive = c2pa::Context(); + auto reader_archive = c2pa::Reader(ctx_reader_archive, "image/jpeg", dest_archive); auto json_archive = json::parse(reader_archive.json()); EXPECT_TRUE(json_archive.contains("active_manifest")); @@ -2972,7 +2974,7 @@ TEST_F(BuilderTest, ArchiveRoundTripSettingsBehaviorRestoredCOntext) // Create context with settings that disable thumbnail generation auto settings_json = c2pa_test::read_text_file(c2pa_test::get_fixture_path("settings/test_settings_no_thumbnail.json")); - auto context_no_thumbnail = c2pa::Context::from_json(settings_json); + auto context_no_thumbnail = c2pa::Context(settings_json); // Verify the setting works when set on builder with context (baseline) auto builder_direct = c2pa::Builder(context_no_thumbnail, manifest); @@ -2985,7 +2987,8 @@ TEST_F(BuilderTest, ArchiveRoundTripSettingsBehaviorRestoredCOntext) }); // Verify no thumbnail is here (aka setting was applied) dest_direct.seekg(0); - auto reader_direct = c2pa::Reader(c2pa::Context::create(), "image/jpeg", dest_direct); + auto ctx_reader_direct = c2pa::Context(); + auto reader_direct = c2pa::Reader(ctx_reader_direct, "image/jpeg", dest_direct); auto json_direct = json::parse(reader_direct.json()); std::string active_direct = json_direct["active_manifest"]; EXPECT_FALSE(json_direct["manifests"][active_direct].contains("thumbnail")) @@ -3011,7 +3014,8 @@ TEST_F(BuilderTest, ArchiveRoundTripSettingsBehaviorRestoredCOntext) }); // Verify manifest structure is preserved dest_archive.seekg(0); - auto reader_archive = c2pa::Reader(c2pa::Context::create(), "image/jpeg", dest_archive); + auto ctx_reader_archive = c2pa::Context(); + auto reader_archive = c2pa::Reader(ctx_reader_archive, "image/jpeg", dest_archive); auto json_archive = json::parse(reader_archive.json()); EXPECT_TRUE(json_archive.contains("active_manifest")); @@ -3027,7 +3031,7 @@ TEST_F(BuilderTest, LoadArchiveWithContext) auto manifest = c2pa_test::read_text_file(c2pa_test::get_fixture_path("training.json")); // Create a context with thumbnail generation disabled - auto context = c2pa::Context::from_json(R"({"builder": {"thumbnail": {"enabled": false}}})"); + auto context = c2pa::Context(R"({"builder": {"thumbnail": {"enabled": false}}})"); // Create a builder with the custom context auto builder1 = c2pa::Builder(context, manifest); @@ -3044,12 +3048,12 @@ TEST_F(BuilderTest, LoadArchiveWithContext) // Create a new builder with a context, then load the archive into it // This preserves the context's settings (thumbnail=false) - auto context2 = c2pa::Context::from_json(R"({"builder": {"thumbnail": {"enabled": false}}})"); + auto context2 = c2pa::Context(R"({"builder": {"thumbnail": {"enabled": false}}})"); archive_stream.seekg(0); c2pa::Builder builder2(context2); // Create a builder with context EXPECT_NO_THROW({ - builder2.load_archive(archive_stream); // Load an archive into the builder + builder2.to_archive(archive_stream); // Load an archive into the builder }); // Sign with the restored builder (which has thumbnail=false from context2) @@ -3064,7 +3068,8 @@ TEST_F(BuilderTest, LoadArchiveWithContext) // Verify the signed asset has NO thumbnail (because context2 had thumbnail=false) dest_stream.seekg(0); - auto reader = c2pa::Reader(c2pa::Context::create(), "image/jpeg", dest_stream); + auto ctx_reader = c2pa::Context(); + auto reader = c2pa::Reader(ctx_reader, "image/jpeg", dest_stream); auto json_result = json::parse(reader.json()); EXPECT_TRUE(json_result.contains("active_manifest")); @@ -3165,7 +3170,8 @@ TEST_F(BuilderTest, MultipleArchivesAsIngredients) // Verify all three ingredients are present with correct relationships dest_stream.seekg(0); - auto reader = c2pa::Reader(c2pa::Context::create(), "image/jpeg", dest_stream); + auto ctx_reader = c2pa::Context(); + auto reader = c2pa::Reader(ctx_reader, "image/jpeg", dest_stream); auto json_result = json::parse(reader.json()); EXPECT_TRUE(json_result.contains("active_manifest")); @@ -3233,7 +3239,8 @@ TEST_F(BuilderTest, AddIngredientFromArchiveStream) // Verify the ingredient is present dest_stream.seekg(0); - auto reader = c2pa::Reader(c2pa::Context::create(), "image/jpeg", dest_stream); + auto ctx_reader = c2pa::Context(); + auto reader = c2pa::Reader(ctx_reader, "image/jpeg", dest_stream); auto json_result = json::parse(reader.json()); EXPECT_TRUE(json_result.contains("active_manifest")); @@ -3285,7 +3292,8 @@ TEST_F(BuilderTest, AddIngredientFromArchiveFile) }); // Verify the ingredient is present - auto reader = c2pa::Reader(c2pa::Context::create(), dest_path); + auto ctx_reader = c2pa::Context(); + auto reader = c2pa::Reader(ctx_reader, dest_path); auto json_result = json::parse(reader.json()); EXPECT_TRUE(json_result.contains("active_manifest")); @@ -3368,7 +3376,8 @@ TEST_F(BuilderTest, AddMultipleArchivesFromArchive) // Verify all three ingredients are present dest_stream.seekg(0); - auto reader = c2pa::Reader(c2pa::Context::create(), "image/jpeg", dest_stream); + auto ctx_reader = c2pa::Context(); + auto reader = c2pa::Reader(ctx_reader, "image/jpeg", dest_stream); auto json_result = json::parse(reader.json()); EXPECT_TRUE(json_result.contains("active_manifest")); @@ -3418,7 +3427,7 @@ TEST_F(BuilderTest, WithDefinitionUpdatesManifest) { fs::path manifest_path = current_dir / "fixtures/training.json"; auto manifest = c2pa_test::read_text_file(manifest_path); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); c2pa::Builder builder(context); builder.with_definition(manifest); @@ -3459,7 +3468,7 @@ TEST_F(BuilderTest, WithDefinitionChaining) { ] })"; auto updated_manifest = c2pa_test::read_text_file(manifest_path); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); c2pa::Builder builder(context, initial_manifest); builder.with_definition(updated_manifest); @@ -3490,7 +3499,7 @@ TEST_F(BuilderTest, WithDefinitionChaining) { } TEST_F(BuilderTest, WithDefinitionInvalidJsonThrows) { - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); c2pa::Builder builder(context); // Invalid JSON should throw C2paException diff --git a/tests/context.test.cpp b/tests/context.test.cpp index a87d3412..b4cab0ce 100644 --- a/tests/context.test.cpp +++ b/tests/context.test.cpp @@ -66,8 +66,8 @@ static std::string load_fixture(const std::string &name) TEST(Context, ContextFromJsonValid) { std::string json = R"({"settings": {}})"; - auto context = c2pa::Context::from_json(json); - ASSERT_NE(context, nullptr); + c2pa::Context context(json); + EXPECT_TRUE(context.has_context()); } // Can create a context using Settings object @@ -75,15 +75,15 @@ TEST(Context, ContextFromSettingsValid) { c2pa::Settings settings; settings.set("builder.thumbnail.enabled", "false"); - auto context = c2pa::Context::from_settings(settings); - ASSERT_NE(context, nullptr); + c2pa::Context context(settings); + EXPECT_TRUE(context.has_context()); } -// Context::from_json() with invalid JSON throws +// Context with invalid JSON throws TEST(Context, ContextFromJsonInvalidThrows) { EXPECT_THROW( - { auto context = c2pa::Context::from_json("{bad"); }, + { c2pa::Context context("{bad"); }, c2pa::C2paException ); } @@ -93,11 +93,11 @@ TEST(Context, SettingsDefaultConstruction) { c2pa::Settings settings; auto manifest = load_fixture("training.json"); - auto context = c2pa::Context::create(); + c2pa::Context context; // Should not crash when building with default settings EXPECT_NO_THROW({ - auto builder = c2pa::Builder(context, manifest); + c2pa::Builder builder(context, manifest); }); } @@ -116,7 +116,7 @@ TEST(Context, ContextBuilderEmptyBuild) auto builder = c2pa::Context::ContextBuilder(); auto context = builder.create_context(); - ASSERT_NE(context, nullptr); + EXPECT_TRUE(context.has_context()); } // Helper function to check if thumbnail is present in signed manifest @@ -127,7 +127,7 @@ static bool has_thumbnail(const std::string& manifest_json) { } // Helper function to sign with context and return manifest JSON -static std::string sign_with_context(std::shared_ptr context, const fs::path& dest_path) { +static std::string sign_with_context(c2pa::IContextProvider& context, const fs::path& dest_path) { fs::path current_dir = fs::path(__FILE__).parent_path(); fs::path manifest_path = current_dir / "fixtures/training.json"; fs::path asset_path = current_dir / "fixtures/A.jpg"; @@ -232,7 +232,7 @@ TEST(Context, ContextBuilderMoveConstructor) { EXPECT_TRUE(b2.is_valid()); auto context = b2.create_context(); - EXPECT_NE(context, nullptr); + EXPECT_TRUE(context.has_context()); } TEST(Context, ContextBuilderMoveAssignment) { @@ -245,7 +245,7 @@ TEST(Context, ContextBuilderMoveAssignment) { EXPECT_TRUE(b1.is_valid()); auto context = b1.create_context(); - EXPECT_NE(context, nullptr); + EXPECT_TRUE(context.has_context()); } TEST(Context, SettingsMoveConstructor) { @@ -258,7 +258,7 @@ TEST(Context, SettingsMoveConstructor) { // Verify s2 is functional auto context = c2pa::Context::ContextBuilder().with_settings(s2).create_context(); - EXPECT_NE(context, nullptr); + EXPECT_TRUE(context.has_context()); } TEST(Context, SettingsMoveAssignment) { @@ -297,3 +297,102 @@ TEST(Context, DoubleConsumeThrows) { (void)context2; // Suppress unused variable warning }, c2pa::C2paException); } + +// Default constructor creates a valid context +TEST(Context, DirectConstructDefault) { + c2pa::Context context; + EXPECT_TRUE(context.has_context()); + EXPECT_NE(context.c_context(), nullptr); +} + +// Constructor with Settings creates a valid context +TEST(Context, DirectConstructWithSettings) { + c2pa::Settings settings; + settings.set("builder.thumbnail.enabled", "false"); + + c2pa::Context context(settings); + EXPECT_TRUE(context.has_context()); +} + +// Default constructor can be used with Builder +TEST(Context, DirectConstructDefaultWithBuilder) { + auto manifest = load_fixture("training.json"); + c2pa::Context context; + + EXPECT_NO_THROW({ + c2pa::Builder builder(context, manifest); + }); +} + +// ============================================================================= +// All creation variations: sign + verify settings propagate +// ============================================================================= + +// 1) Direct construction with Settings — sign and verify thumbnail is disabled +TEST_F(ContextTest, DirectConstructSettingsSignVerify) { + c2pa::Settings settings; + settings.set("builder.thumbnail.enabled", "false"); + + c2pa::Context context(settings); + auto manifest_json = sign_with_context(context, get_temp_path("direct_construct_settings.jpg")); + + EXPECT_FALSE(has_thumbnail(manifest_json)); +} + +// 2) Direct default construction — sign and verify thumbnail is enabled (default) +TEST_F(ContextTest, DirectConstructDefaultSignVerify) { + c2pa::Context context; + auto manifest_json = sign_with_context(context, get_temp_path("direct_construct_default.jpg")); + + EXPECT_TRUE(has_thumbnail(manifest_json)); +} + +// 3) JSON string constructor — sign and verify thumbnail is disabled +TEST_F(ContextTest, JsonConstructorSignVerify) { + c2pa::Context context(R"({"builder": {"thumbnail": {"enabled": false}}})"); + auto manifest_json = sign_with_context(context, get_temp_path("json_constructor.jpg")); + + EXPECT_FALSE(has_thumbnail(manifest_json)); +} + +// 4) ContextBuilder with Settings — sign and verify thumbnail is disabled +TEST_F(ContextTest, ContextBuilderWithSettingsSignVerify) { + c2pa::Settings settings; + settings.set("builder.thumbnail.enabled", "false"); + + auto context = c2pa::Context::ContextBuilder() + .with_settings(settings) + .create_context(); + auto manifest_json = sign_with_context(context, get_temp_path("builder_with_settings.jpg")); + + EXPECT_FALSE(has_thumbnail(manifest_json)); +} + +// 5) ContextBuilder with JSON — sign and verify thumbnail is disabled +TEST_F(ContextTest, ContextBuilderWithJsonSignVerify) { + auto context = c2pa::Context::ContextBuilder() + .with_json(R"({"builder": {"thumbnail": {"enabled": false}}})") + .create_context(); + auto manifest_json = sign_with_context(context, get_temp_path("builder_with_json.jpg")); + + EXPECT_FALSE(has_thumbnail(manifest_json)); +} + +// 6) ContextBuilder empty (default) — sign and verify thumbnail is enabled (default) +TEST_F(ContextTest, ContextBuilderDefaultSignVerify) { + auto context = c2pa::Context::ContextBuilder().create_context(); + auto manifest_json = sign_with_context(context, get_temp_path("builder_default.jpg")); + + EXPECT_TRUE(has_thumbnail(manifest_json)); +} + +// 7) Direct construction with Settings enabling thumbnail — sign and verify +TEST_F(ContextTest, DirectConstructSettingsEnableThumbnailSignVerify) { + c2pa::Settings settings; + settings.set("builder.thumbnail.enabled", "true"); + + c2pa::Context context(settings); + auto manifest_json = sign_with_context(context, get_temp_path("direct_construct_enable_thumb.jpg")); + + EXPECT_TRUE(has_thumbnail(manifest_json)); +} diff --git a/tests/cpp-app-test/test.cpp b/tests/cpp-app-test/test.cpp index c664fe94..11a11135 100644 --- a/tests/cpp-app-test/test.cpp +++ b/tests/cpp-app-test/test.cpp @@ -60,7 +60,7 @@ int main() try { // Create default context (can be used for basic operations) - auto default_context = c2pa::Context::create(); + auto default_context = c2pa::Context(); std::ifstream ifs("tests/fixtures/C.jpg", std::ios::binary); if (!ifs) @@ -105,7 +105,7 @@ int main() std::remove(signed_path); // Create default context for basic operations - auto default_context = c2pa::Context::create(); + auto default_context = c2pa::Context(); // Create builder with context auto builder = c2pa::Builder(default_context, manifest); @@ -142,7 +142,7 @@ int main() const char *signed_path = "target/tmp/C_signed-stream.jpg"; std::remove(signed_path); - auto default_context = c2pa::Context::create(); + auto default_context = c2pa::Context(); auto builder = c2pa::Builder(default_context, manifest); std::ifstream source("tests/fixtures/C.jpg", std::ios::binary); @@ -220,7 +220,7 @@ int main() cout << "Read manifest with trust validation" << endl; // Compare: read without trust context - will only show "Valid" at best - auto default_context2 = c2pa::Context::create(); + auto default_context2 = c2pa::Context(); auto basic_reader = c2pa::Reader(default_context2, trusted_signed_path); string basic_json = basic_reader.json(); diff --git a/tests/embeddable.test.cpp b/tests/embeddable.test.cpp index abe27a4e..ff875ed5 100644 --- a/tests/embeddable.test.cpp +++ b/tests/embeddable.test.cpp @@ -71,7 +71,7 @@ TEST_F(EmbeddableTest, FullWorkflowWithAJpg) { auto source_asset = c2pa_test::get_fixture_path("A.jpg"); // Create signing infra - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json); c2pa::Signer signer("Es256", certs, private_key, "http://timestamp.digicert.com"); @@ -126,7 +126,7 @@ TEST_F(EmbeddableTest, FullWorkflowWithCJpg) { auto signer = c2pa_test::create_test_signer(); auto source_asset = c2pa_test::get_fixture_path("C.jpg"); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json); // Get placeholder @@ -169,7 +169,7 @@ TEST_F(EmbeddableTest, PreCalculatedHash) { auto manifest_json = c2pa_test::read_text_file(c2pa_test::get_fixture_path("training.json")); auto signer = c2pa_test::create_test_signer(); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json); // Get placeholder @@ -213,7 +213,7 @@ TEST_F(EmbeddableTest, AutoCalculatedHash) { auto signer = c2pa_test::create_test_signer(); auto source_asset = c2pa_test::get_fixture_path("A.jpg"); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json); // Get placeholder @@ -254,7 +254,7 @@ TEST_F(EmbeddableTest, FormatEmbeddableRoundTrip) { auto signer = c2pa_test::create_test_signer(); auto source_asset = c2pa_test::get_fixture_path("A.jpg"); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json); auto placeholder = builder.data_hashed_placeholder(signer.reserve_size(), "image/jpeg"); @@ -297,7 +297,7 @@ TEST_F(EmbeddableTest, PlaceholderSizeMatchesFinalInvariant) { auto signer = c2pa_test::create_test_signer(); auto source_asset = c2pa_test::get_fixture_path("A.jpg"); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json); // Get placeholder and record its size @@ -335,7 +335,7 @@ TEST_F(EmbeddableTest, PlaceholderSizeMatchesFinalInvariantWithMetadata) { auto signer = c2pa_test::create_test_signer(); auto source_asset = c2pa_test::get_fixture_path("C.jpg"); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json); auto placeholder = builder.data_hashed_placeholder(signer.reserve_size(), "image/jpeg"); @@ -367,7 +367,7 @@ TEST_F(EmbeddableTest, ContextSettingsPropagation) { auto source_asset = c2pa_test::get_fixture_path("A.jpg"); // Create context with custom settings - auto context = c2pa::Context::from_json(R"({ + auto context = c2pa::Context(R"({ "builder": { "thumbnail": { "enabled": false @@ -408,7 +408,7 @@ TEST_F(EmbeddableTest, ArchiveRoundTripWithContextAJpg) { auto signer = c2pa_test::create_test_signer(); // Create context with custom settings (thumbnails disabled) - auto context = c2pa::Context::from_json(R"({ + auto context = c2pa::Context(R"({ "builder": { "thumbnail": { "enabled": false @@ -430,7 +430,7 @@ TEST_F(EmbeddableTest, ArchiveRoundTripWithContextAJpg) { // Load from archive using same context auto builder2 = c2pa::Builder(context); std::ifstream load_stream(archive_path, std::ios::binary); - builder2.load_archive(load_stream); + builder2.with_archive(load_stream); load_stream.close(); // Get placeholder from restored builder @@ -447,7 +447,7 @@ TEST_F(EmbeddableTest, ArchiveRoundTripWithContextCJpg) { auto signer = c2pa_test::create_test_signer(); auto source_asset = c2pa_test::get_fixture_path("C.jpg"); - auto context = c2pa::Context::from_json(R"({ + auto context = c2pa::Context(R"({ "builder": { "thumbnail": { "enabled": false @@ -464,7 +464,7 @@ TEST_F(EmbeddableTest, ArchiveRoundTripWithContextCJpg) { auto builder2 = c2pa::Builder(context); std::ifstream load_stream(archive_path, std::ios::binary); - builder2.load_archive(load_stream); + builder2.with_archive(load_stream); load_stream.close(); auto placeholder = builder2.data_hashed_placeholder(signer.reserve_size(), "image/jpeg"); @@ -519,7 +519,7 @@ TEST_F(EmbeddableTest, ArchiveWithIngredientAJpg) { ] })"; - auto context = c2pa::Context::from_json(R"({ + auto context = c2pa::Context(R"({ "builder": { "thumbnail": { "enabled": false @@ -545,7 +545,7 @@ TEST_F(EmbeddableTest, ArchiveWithIngredientAJpg) { auto builder2 = c2pa::Builder(context); std::ifstream load_stream(archive_path, std::ios::binary); - builder2.load_archive(load_stream); + builder2.with_archive(load_stream); load_stream.close(); // Get placeholder from restored builder @@ -583,7 +583,7 @@ TEST_F(EmbeddableTest, ArchiveWithIngredientCJpg) { ] })"; - auto context = c2pa::Context::from_json(R"({ + auto context = c2pa::Context(R"({ "builder": { "thumbnail": { "enabled": false @@ -603,7 +603,7 @@ TEST_F(EmbeddableTest, ArchiveWithIngredientCJpg) { auto builder2 = c2pa::Builder(context); std::ifstream load_stream(archive_path, std::ios::binary); - builder2.load_archive(load_stream); + builder2.with_archive(load_stream); load_stream.close(); // Verify we can create placeholders and sign after round-trip @@ -632,7 +632,7 @@ TEST_F(EmbeddableTest, MultipleFormats) { auto manifest_json = c2pa_test::read_text_file(c2pa_test::get_fixture_path("training.json")); auto signer = c2pa_test::create_test_signer(); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); // jpeg { @@ -693,7 +693,7 @@ TEST_F(EmbeddableTest, DirectEmbeddingWithFormat) { auto signer = c2pa_test::create_test_signer(); auto source_asset = c2pa_test::get_fixture_path("A.jpg"); - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto builder = c2pa::Builder(context, manifest_json); auto placeholder = builder.data_hashed_placeholder(signer.reserve_size(), "image/jpeg"); diff --git a/tests/reader.test.cpp b/tests/reader.test.cpp index 31f2fdbc..359ce8df 100644 --- a/tests/reader.test.cpp +++ b/tests/reader.test.cpp @@ -131,7 +131,7 @@ TEST_F(ReaderTest, MultipleReadersSameFileUsingContext) ASSERT_TRUE(std::filesystem::exists(test_file)) << "Test file does not exist: " << test_file; // Create a Context - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); // create multiple readers from the same file using the context auto reader1 = c2pa::Reader(context, test_file); @@ -186,7 +186,7 @@ TEST_F(ReaderTest, VideoStreamWithManifestUsingExtensionUsingContext) { ASSERT_TRUE(file_stream.is_open()) << "Failed to open video file: " << test_file; // Create a Context and pass it to the Reader - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto reader = c2pa::Reader(context, "mp4", file_stream); auto manifest_store_json = reader.json(); EXPECT_TRUE(manifest_store_json.find("My Title") != std::string::npos); @@ -303,7 +303,7 @@ TEST_F(ReaderTest, HasManifestUtf8PathUsingContext) { std::ifstream stream(test_file, std::ios::binary); // Create a Context and pass it to the Reader - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); auto reader = c2pa::Reader(context, "image/jpeg", stream); EXPECT_FALSE(reader.remote_url()); @@ -354,7 +354,7 @@ TEST_F(ReaderTest, ReadManifestWithTrustConfiguredJsonSettings) // already configured with that trust to use with our Builder and Reader. fs::path settings_path = current_dir / "../tests/fixtures/settings/test_settings_example.json"; auto settings = c2pa_test::read_text_file(settings_path); - auto trusted_context = c2pa::Context::from_json(settings); + auto trusted_context = c2pa::Context(settings); // When reading, the Reader also needs to know about trust, to determine the manifest validation state // If there is a valid trust chain, the manifest will be in validation_state Trusted. @@ -378,7 +378,7 @@ TEST_F(ReaderTest, ReaderFromIStreamWithContext) GTEST_SKIP() << "Fixture not found: " << signed_path; } - auto context = c2pa::Context::create(); + auto context = c2pa::Context(); std::ifstream stream(signed_path, std::ios::binary); ASSERT_TRUE(stream) << "Failed to open " << signed_path; @@ -442,7 +442,7 @@ TEST(ReaderErrorHandling, EmptyStreamBehavesTheSameWithAndWithoutContext) }, c2pa::C2paException); // With context - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); EXPECT_THROW({ c2pa::Reader reader(ctx, format, empty_stream2); }, c2pa::C2paException); @@ -458,7 +458,7 @@ TEST(ReaderErrorHandling, NonExistentFileBehavesTheSameWithAndWithoutContext) }, std::system_error); // With context - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); EXPECT_THROW({ c2pa::Reader reader(ctx, nonexistent); }, std::system_error); @@ -483,7 +483,7 @@ TEST(ReaderErrorHandling, InvalidStreamBehavesTheSameWithAndWithoutContext) } try { - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); c2pa::Reader reader(ctx, format, stream2); } catch (...) { with_context_throws = true; @@ -509,7 +509,7 @@ TEST(ReaderErrorHandling, FailedReaderConstructionWithAndWithoutContext) // With context try { - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); c2pa::Reader reader(ctx, format, stream2); } catch (...) { // Expected to fail on empty stream @@ -533,7 +533,7 @@ TEST(ReaderErrorHandling, ErrorMessagesWithAndWithoutContext) // With context try { - auto ctx = c2pa::Context::create(); + auto ctx = c2pa::Context(); c2pa::Reader reader(ctx, format, empty_stream2); FAIL() << "Should have thrown"; } catch (const c2pa::C2paException& e) {