Skip to content

Commit c58f3c7

Browse files
committed
Added an edited-store transfer regression covering JPEG, TIFF, DNG, JXL, WebP, PNG, JP2, HEIF, AVIF, CR3, and EXR
Added an edited-store transfer regression covering JPEG, TIFF, DNG, JXL, WebP, PNG, JP2, HEIF, AVIF, CR3, and EXR in OpenMeta/tests/metadata_editing_transfer_test.cc:1. It verifies replacements/additions reach each target backend and removed values do not return. EXR remains intentionally limited to safe mapped attributes, not embedded XMP.
1 parent 9d111ae commit c58f3c7

5 files changed

Lines changed: 160 additions & 1 deletion

File tree

‎CHANGES.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# OpenMeta Changes
22

3+
## 0.4.93 - 2026-08-03
4+
5+
Changes compared with `0.4.92`.
6+
7+
### Added
8+
9+
- Added a transfer regression that feeds logical metadata edits through every
10+
public target preparation and execution backend.
11+
- Verified edited values reach JPEG, TIFF/DNG, JXL, WebP, PNG, JP2, HEIF,
12+
AVIF, CR3, and EXR routes while removed values do not reappear.
13+
314
## 0.4.92 - 2026-08-02
415

516
Changes compared with `0.4.91`.

‎CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,7 @@ if(OPENMETA_BUILD_TESTS)
829829
tests/metadata_concepts_test.cc
830830
tests/metadata_creation_test.cc
831831
tests/metadata_editing_test.cc
832+
tests/metadata_editing_transfer_test.cc
832833
tests/metadata_fuzzy_search_test.cc
833834
tests/metadata_interpretation_test.cc
834835
tests/metadata_query_test.cc

‎VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.4.92
1+
0.4.93

‎docs/metadata_transfer_plan.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ The first public write-side sync controls are also in place:
4747
- prepared bundles record those resolved projection decisions alongside the
4848
existing preservation policies
4949

50+
Logical metadata edits are covered through preparation and typed backend
51+
execution for every public target: JPEG, TIFF/DNG, JXL, WebP, PNG, JP2, HEIF,
52+
AVIF, CR3, and EXR. The regression verifies an edited safe camera descriptor
53+
reaches every target, while title, creator, and keyword edits reach every
54+
XMP-capable target and replaced or removed values are not serialized again.
55+
TIFF/DNG and EXR execution intentionally uses their typed writer or adapter
56+
contracts rather than a generic byte stream. EXR does not embed XMP, so its
57+
safe string-attribute projection is intentionally narrower than the
58+
XMP-capable targets.
59+
5060
## Target Status Matrix
5161

5262
| Target | Status | Current shape | Main limits |
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "openmeta/metadata_creation.h"
4+
#include "openmeta/metadata_editing.h"
5+
#include "openmeta/metadata_transfer.h"
6+
7+
#include <gtest/gtest.h>
8+
9+
#include <array>
10+
#include <cstddef>
11+
#include <span>
12+
#include <string_view>
13+
14+
namespace openmeta {
15+
namespace {
16+
17+
struct TransferTargetCase final {
18+
TransferTargetFormat format;
19+
const char* name;
20+
};
21+
22+
static MetaStore make_edited_store()
23+
{
24+
const std::array fields = {
25+
make_metadata_creation_text(MetadataCreationFieldKind::Title,
26+
"Before"),
27+
make_metadata_creation_text(MetadataCreationFieldKind::Creator,
28+
"Alice"),
29+
make_metadata_creation_text(MetadataCreationFieldKind::Creator,
30+
"Bob"),
31+
make_metadata_creation_text(MetadataCreationFieldKind::Keyword,
32+
"night"),
33+
make_metadata_creation_text(MetadataCreationFieldKind::CameraMake,
34+
"Vendor Before"),
35+
};
36+
MetadataCreationRequest creation_request;
37+
creation_request.fields = fields;
38+
MetaStore source;
39+
EXPECT_EQ(create_metadata(creation_request, &source).status,
40+
MetadataCreationStatus::Ok);
41+
42+
const std::array operations = {
43+
make_metadata_edit_set(
44+
make_metadata_creation_text(MetadataCreationFieldKind::Title,
45+
"After")),
46+
make_metadata_edit_set(
47+
make_metadata_creation_text(MetadataCreationFieldKind::Creator,
48+
"Carol"),
49+
1U),
50+
make_metadata_edit_add(
51+
make_metadata_creation_text(MetadataCreationFieldKind::Keyword,
52+
"city")),
53+
make_metadata_edit_remove(MetadataCreationFieldKind::Creator, 0U),
54+
make_metadata_edit_set(make_metadata_creation_text(
55+
MetadataCreationFieldKind::CameraMake, "Vendor After")),
56+
};
57+
MetadataEditingRequest editing_request;
58+
editing_request.operations = operations;
59+
MetaStore edited;
60+
EXPECT_EQ(edit_metadata(source, editing_request, &edited).status,
61+
MetadataEditingStatus::Ok);
62+
return edited;
63+
}
64+
65+
static bool bundle_contains_text(const PreparedTransferBundle& bundle,
66+
std::string_view text) noexcept
67+
{
68+
for (const PreparedTransferBlock& block : bundle.blocks) {
69+
const std::string_view payload(reinterpret_cast<const char*>(
70+
block.payload.data()),
71+
block.payload.size());
72+
if (payload.find(text) != std::string_view::npos) {
73+
return true;
74+
}
75+
}
76+
return false;
77+
}
78+
79+
static bool
80+
bundle_contains_removed_text(const PreparedTransferBundle& bundle) noexcept
81+
{
82+
return bundle_contains_text(bundle, "Before")
83+
|| bundle_contains_text(bundle, "Alice");
84+
}
85+
86+
TEST(MetadataEditingTransfer,
87+
EditedStoreReachesEveryTargetPreparationAndExecutionBackend)
88+
{
89+
const MetaStore edited = make_edited_store();
90+
const std::array targets = {
91+
TransferTargetCase { TransferTargetFormat::Jpeg, "jpeg" },
92+
TransferTargetCase { TransferTargetFormat::Tiff, "tiff" },
93+
TransferTargetCase { TransferTargetFormat::Dng, "dng" },
94+
TransferTargetCase { TransferTargetFormat::Jxl, "jxl" },
95+
TransferTargetCase { TransferTargetFormat::Webp, "webp" },
96+
TransferTargetCase { TransferTargetFormat::Png, "png" },
97+
TransferTargetCase { TransferTargetFormat::Jp2, "jp2" },
98+
TransferTargetCase { TransferTargetFormat::Heif, "heif" },
99+
TransferTargetCase { TransferTargetFormat::Avif, "avif" },
100+
TransferTargetCase { TransferTargetFormat::Cr3, "cr3" },
101+
TransferTargetCase { TransferTargetFormat::Exr, "exr" },
102+
};
103+
104+
for (const TransferTargetCase& target : targets) {
105+
SCOPED_TRACE(target.name);
106+
PrepareTransferRequest request;
107+
request.target_format = target.format;
108+
request.include_exif_app1 = false;
109+
request.include_icc_app2 = false;
110+
request.include_iptc_app13 = false;
111+
request.xmp_project_exif = false;
112+
request.xmp_project_iptc = false;
113+
114+
PreparedTransferBundle bundle;
115+
const PrepareTransferResult prepared
116+
= prepare_metadata_for_target(edited, request, &bundle);
117+
ASSERT_EQ(prepared.status, TransferStatus::Ok);
118+
ASSERT_FALSE(bundle.blocks.empty());
119+
EXPECT_TRUE(bundle_contains_text(bundle, "After"));
120+
EXPECT_FALSE(bundle_contains_removed_text(bundle));
121+
if (target.format != TransferTargetFormat::Exr) {
122+
EXPECT_TRUE(bundle_contains_text(bundle, "Carol"));
123+
EXPECT_TRUE(bundle_contains_text(bundle, "city"));
124+
}
125+
126+
// TIFF/DNG and EXR intentionally use typed writer or adapter
127+
// backends instead of a generic byte stream.
128+
const ExecutePreparedTransferResult executed
129+
= execute_prepared_transfer(&bundle);
130+
EXPECT_EQ(executed.compile.status, TransferStatus::Ok);
131+
EXPECT_EQ(executed.emit.status, TransferStatus::Ok);
132+
EXPECT_GT(executed.compiled_ops, 0U);
133+
}
134+
}
135+
136+
} // namespace
137+
} // namespace openmeta

0 commit comments

Comments
 (0)