Fix stale instrument-change keys after editing or deleting a preceding key in linked scores - #34848
Fix stale instrument-change keys after editing or deleting a preceding key in linked scores#348480xMashiro wants to merge 2 commits into
Conversation
Dispatch later instrument-change key updates on each linked staff owner when changing an explicit key. Traverse linked staves independently after key deletion so the other score does not retain a stale later key. Add separate edit and delete regressions covering master and excerpt entry points, DOM/key-map agreement, undo, redo, integrity checks, and MSCZ save/reopen. All 12 native instrument-change/key tests pass. Both new tests fail on the unmodified upstream baseline; equivalent desktop before/after recordings confirm both paths. Refs: musescore#34847
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe change routes instrument-change transposition updates through the score that owns each linked staff. Key-signature deletion now updates every linked staff score. An assertion verifies the staff belongs to the receiving score. New fixture data and tests cover preceding key changes and deletions across master scores and excerpts, including undo and redo. Priority: ➖ Normal Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to Linked full scores and parts now keep instrument-change keys synchronized after preceding key edits or deletions, with regression coverage for both entry points and undo/redo. No current merge-blocking risk remains. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 4 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Linked repositories: Public OSS repositories can only analyze public repositories installed in this organization. No linked repositories were analyzed; skipped Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ajuncosa
left a comment
There was a problem hiding this comment.
The fix itself seems reasonable. However, the tests need some re-working or at least some explanation:
- the bug is about in-memory propagation across linked scores. Is it really necessary to do a
MscWriter/MscSaverround-trip for this specific case? If you do want to do it for some reason, maybe it'd be cleaner to just use theScoreComp::saveCompareScore()helper? Test mode already inlines excerpts into the master's XML. - I don't think the
.mscztest files are minimal examples for the specific tests (e.g. unused measures and parts). We could probably even use a single.mscxfor both tests. - nitpick: the fix is in in
editkeysig.cppand the keysig delete path, sokeysig_tests.cppmight be a better place to put them? - see inline comments
| { | ||
| // Write production MSCZ excerpts separately, without test-mode inline excerpts. | ||
| const bool testMode = MScore::testMode; | ||
| DEFER { MScore::testMode = testMode; |
There was a problem hiding this comment.
why toggle the the global MScore::testMode inside a test?
There was a problem hiding this comment.
The toggle was intended to exercise separately stored MSCZ excerpts, but that is unnecessary for this in-memory regression. I removed the custom archive round-trip and the test-mode toggle; the earlier save/reopen check remains supplementary evidence.
| #include "global/defer.h" | ||
| #include "global/io/buffer.h" | ||
| #include "global/io/file.h" | ||
| #include "engraving/rw/mscsaver.h" | ||
| #include "engraving/dom/mscore.h" | ||
| #include "engraving/dom/excerpt.h" | ||
| #include "engraving/dom/keysig.h" | ||
| #include "engraving/editing/editkeysig.h" | ||
| #include "engraving/editing/transaction/transaction.h" |
There was a problem hiding this comment.
please review our coding guidelines for sorting/grouping recommendations and ensure only needed things are included
There was a problem hiding this comment.
Removed the archive-related includes and moved the regressions to keysig_tests.cpp. The remaining includes are grouped and sorted according to the guidelines.
| KeySigEvent key; | ||
| key.setConcertKey(Key::G); | ||
| score->startCmd(TranslatableString::untranslatable("Change preceding key")); | ||
| EditKeySig::undoChangeKeySig(score->transactionManager()->currentOrDummyTransaction(), owner, owner->staff(0), Fraction(0, |
There was a problem hiding this comment.
EditKeySig::undoChangeKeySig isn't really covering the same scope as owner->cmdDeleteSelection() (the change case vs. the delete case). Is this intended? Can you explain what we're really testing in these two tests?
There was a problem hiding this comment.
Yes: the change test targets propagation in EditKeySig::undoChangeKeySig, following the existing keysig test. Deletion needs the Score::deleteItem path reached by cmdDeleteSelection; removing the element alone would miss the update being tested. I split the boolean-driven helper into two explicit tests. Both now edit the same mid-measure key, run from the master and part, and check both scores after the operation, undo, and redo.
Move the change/delete cases to the key-signature suite and share a minimal two-measure MSCX using TestUtils::createPart. Keep each command path explicit and check both scores through undo and redo. Remove custom archive writing and global test-mode changes; assert the staff/score ownership invariant. The same final regressions fail on the unpatched baseline while its 10 existing tests pass. All 12 key-signature/instrument-change tests pass with the fix. Both production translation units compile independently, and the changed code passes official formatting and diff checks. Refs: musescore#34847
|
Updated in 2b81b17. The regressions now live in With the same final tests, the unpatched baseline passes the 10 existing tests and fails both new regressions on stale keys. The fix passes all 12 tests. Standalone compilation of both production translation units and the formatting checks also pass. The existing recordings remain supplementary evidence using the original desktop examples. The PR description now distinguishes those from the smaller regression fixture. @ajuncosa, could you take another look when you have time? |
Resolves: #34847
Changing or deleting an explicit key before an instrument change can leave the later instrument-change key stale in the other linked score. For the supplied flute-to-B-flat-clarinet examples, editing the opening key updates the part's later key to three sharps while the full score keeps two; deleting the preceding key restores two sharps in the part while the full score keeps three.
Dispatch subsequent instrument-change key updates on each linked staff's owning score in
EditKeySig::undoChangeKeySig. Independently, follow linked staves after key deletion and update each owning score. The change preserves existing global/local key policy and the helper's stopping rule at the next explicit key.Validation
Engraving_InstrumentChangeTests.*:Engraving_KeySigTests.*— 12 tests pass (Ubuntu, Qt 6.10.2, GCC 14, Debug/ASan).keysig_tests.cppshare a two-measure, one-staff MSCX and create the linked part withTestUtils::createPart. They separately change and delete the preceding key, entering from both the full score and part, and check the key map and key-signature element in both scores before/after the edit, undo, and redo.a64e0d0c8725a89af2b4b9f8e058a75be20c01d3with stale instrument-change keys. Both production corrections are retained; an ownership assertion now documents the receiver/staff invariant.git diff --checkpass.Before and after recordings
These recordings use the original desktop examples; the regression tests now use the smaller fixture described above.
Change a preceding key — Before:
change-before.mp4
Change a preceding key — After:
change-after.mp4
Delete a preceding key — Before:
delete-before.mp4
Delete a preceding key — After:
delete-after.mp4
Related work and review scope
Related reports #18590 item 5 (same-score concert-pitch transposition) and #23934 (global keys across different instruments) are outside the claimed fix. Historical PRs #23053, #23975 and #26315 are closed and unmerged; no active matching fix was identified in the bounded search.
AI was used to improve efficiency during development.
Contributor checklist