[FLAC] Track iXML/BEXT block presence with explicit flags

hasiXMLData() / hasBEXTData() were implemented as !data.isEmpty()
checks, which conflated in-memory payload with on-disk block presence.
That caused two wrong answers:

* setiXMLData("foo") on a file with no iXML block made hasiXMLData()
  return true immediately, before save().
* A FLAC file carrying an iXML APPLICATION block with empty payload
  round-tripped fine, but hasiXMLData() reported false.

Switch to the same model RIFF::WAV::File already uses: explicit
hasiXML / hasBEXT bool flags on FilePrivate, set during scan() when
the APPLICATION block is recognised, updated during save() after the
block is (re)written or omitted, and returned verbatim by the
accessors. New regression test pins down the before/after-save and
empty-block cases.

Refs: https://github.com/taglib/taglib/issues/1362
This commit is contained in:
Ryan Francesconi
2026-05-16 09:18:11 -07:00
committed by Urs Fleisch
parent e23d97c580
commit c32f7c7f86
2 changed files with 58 additions and 4 deletions

View File

@@ -74,6 +74,7 @@ class TestFLAC : public CppUnit::TestFixture
CPPUNIT_TEST(testReadBEXTRiffWrapped);
CPPUNIT_TEST(testWriteiXMLAndBEXT);
CPPUNIT_TEST(testWriteEmptyClearsiXMLAndBEXT);
CPPUNIT_TEST(testHasiXMLAndBEXTReflectFileState);
CPPUNIT_TEST(testRoundTripPreservesUnknownApplicationBlock);
CPPUNIT_TEST_SUITE_END();
@@ -833,6 +834,45 @@ public:
}
}
void testHasiXMLAndBEXTReflectFileState()
{
// hasiXMLData() / hasBEXTData() must report whether the *file* carries an
// iXML / bext APPLICATION block, not whether in-memory payload happens to
// be non-empty. Regression test for an issue where the accessors were
// implemented as !data.isEmpty() and so flipped on as soon as set*Data()
// was called, before save(), and missed a real-but-empty block.
ScopedFileCopy copy("silence-44-s", ".flac");
const string newname = copy.fileName();
{
FLAC::File f(newname.c_str());
CPPUNIT_ASSERT(!f.hasiXMLData());
CPPUNIT_ASSERT(!f.hasBEXTData());
f.setiXMLData("<BWFXML/>");
f.setBEXTData(ByteVector("bext"));
// File hasn't been saved yet — file still has no blocks.
CPPUNIT_ASSERT(!f.hasiXMLData());
CPPUNIT_ASSERT(!f.hasBEXTData());
f.save();
// After save the blocks are on disk.
CPPUNIT_ASSERT(f.hasiXMLData());
CPPUNIT_ASSERT(f.hasBEXTData());
}
{
FLAC::File f(newname.c_str());
CPPUNIT_ASSERT(f.hasiXMLData());
CPPUNIT_ASSERT(f.hasBEXTData());
f.setiXMLData(String());
f.setBEXTData(ByteVector());
// In-memory payload now empty but the file still carries both blocks.
CPPUNIT_ASSERT(f.hasiXMLData());
CPPUNIT_ASSERT(f.hasBEXTData());
f.save();
CPPUNIT_ASSERT(!f.hasiXMLData());
CPPUNIT_ASSERT(!f.hasBEXTData());
}
}
void testRoundTripPreservesUnknownApplicationBlock()
{
// Source: silence-44-s with an extra APPLICATION/"SMED" block injected