Skip to content

Engraving tests: rewrite ScoreComp::compareFiles without diff#33822

Draft
RomanPudashkin wants to merge 2 commits into
musescore:mainfrom
RomanPudashkin:new_compare_files
Draft

Engraving tests: rewrite ScoreComp::compareFiles without diff#33822
RomanPudashkin wants to merge 2 commits into
musescore:mainfrom
RomanPudashkin:new_compare_files

Conversation

@RomanPudashkin

Copy link
Copy Markdown
Contributor

Advantages:

  • Easier to set up the dev environment on Windows
  • Tests may run faster since there is no longer a need to launch diff as a separate process

@RomanPudashkin

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

ScoreComp::compareFiles is rewritten to operate entirely in-process. Both files are read into line lists via a new readLines helper (UTF-8, CR-stripped). When lines differ, an LCS dynamic-programming algorithm (computeDiff) produces an edit sequence, and a unified diff with @@ hunk headers is printed to stdout; the method returns false. When lines match it returns true. The ScoreComp header gains #pragma once, a static injected IFileSystem member, and the readLines private declaration. The bb and bww import/export test modules update their CMakeLists to reference scorecomp/scorerw sources via a new ENGRAVING_TEST_UTILS_DIR variable, and their testbase.cpp files replace the QProcess-based diff invocation with a direct ScoreComp::compareFiles call.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete and lacks required sections from the template (CLA, commit messages, testing, code review checklist). Complete the PR description template with CLA confirmation, commit message details, testing verification, and all required checkboxes marked.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: rewriting ScoreComp::compareFiles to eliminate the diff process dependency.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 1 linked repositories, but your current plan allows 0. Analyzed ``, skipped musescore/muse_framework.git.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/engraving/tests/utils/scorecomp.cpp (1)

55-56: 🏗️ Heavy lift

The LCS DP table is quadratic in memory and can blow up on larger files.

computeDiff() allocates (m+1)*(n+1) size_t entries; for larger score snapshots this can become hundreds of MB/GB and hurt CI reliability. Consider a linear-space diff strategy (or a size guard + fallback path).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engraving/tests/utils/scorecomp.cpp` around lines 55 - 56, The
`computeDiff()` function allocates a dp table with quadratic memory usage of
(m+1)*(n+1) size_t entries, which can consume hundreds of MB/GB for large files
and impact CI reliability. Refactor this to use a linear-space diff strategy
such as Hirschberg's algorithm instead of the standard dynamic programming
approach, or alternatively add a size guard that checks if m*n exceeds a
reasonable threshold and falls back to a more memory-efficient diff algorithm
when exceeded. Update the dp vector allocation and the LCS computation logic to
use the optimized approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/engraving/tests/utils/scorecomp.cpp`:
- Around line 210-216: The readLines() function masks file I/O errors by
returning an empty list instead of propagating the error to its caller. Change
the return type of readLines() from StringList to RetVal<StringList> so that
when the file read fails (when !rv.ret is true), the function propagates the
error via the RetVal wrapper rather than returning an empty list. This allows
compareFiles() to properly distinguish between truly empty files and files that
failed to read, preventing false-positive test results.

---

Nitpick comments:
In `@src/engraving/tests/utils/scorecomp.cpp`:
- Around line 55-56: The `computeDiff()` function allocates a dp table with
quadratic memory usage of (m+1)*(n+1) size_t entries, which can consume hundreds
of MB/GB for large files and impact CI reliability. Refactor this to use a
linear-space diff strategy such as Hirschberg's algorithm instead of the
standard dynamic programming approach, or alternatively add a size guard that
checks if m*n exceeds a reasonable threshold and falls back to a more
memory-efficient diff algorithm when exceeded. Update the dp vector allocation
and the LCS computation logic to use the optimized approach.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a674c25c-1105-448c-91fc-83f4f780bffd

📥 Commits

Reviewing files that changed from the base of the PR and between 3ef5acb and 923c507.

📒 Files selected for processing (6)
  • src/engraving/tests/utils/scorecomp.cpp
  • src/engraving/tests/utils/scorecomp.h
  • src/importexport/bb/tests/CMakeLists.txt
  • src/importexport/bb/tests/testbase.cpp
  • src/importexport/bww/tests/CMakeLists.txt
  • src/importexport/bww/tests/testbase.cpp

Comment on lines +210 to 216
StringList ScoreComp::readLines(const String& path)
{
RetVal<ByteArray> rv = fileSystem()->readFile(path);
if (!rv.ret) {
LOGE() << "Failed to read: " << path << ", err: " << rv.ret.toString();
return {};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Propagate file-read failures instead of returning an empty line list.

readLines() currently returns {} on I/O error, which makes compareFiles() treat unreadable files as equal in some cases (e.g., both reads fail, or failed read vs truly empty file). This can produce false-positive passing tests now that bb/bww delegate to ScoreComp::compareFiles().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engraving/tests/utils/scorecomp.cpp` around lines 210 - 216, The
readLines() function masks file I/O errors by returning an empty list instead of
propagating the error to its caller. Change the return type of readLines() from
StringList to RetVal<StringList> so that when the file read fails (when !rv.ret
is true), the function propagates the error via the RetVal wrapper rather than
returning an empty list. This allows compareFiles() to properly distinguish
between truly empty files and files that failed to read, preventing
false-positive test results.

QTextStream out(stdout);
out << p.readAllStandardOutput();
err << p.readAllStandardError();
StringList lines = String::fromUtf8(rv.val).split(Char(u'\n'));

@cbjeukendrup cbjeukendrup Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be nice to avoid muse::String, to avoid the 8-bit -> 16-bit conversion, for another speed up

Could perhaps also use std::string_view, to avoid copying into the list of lines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants