#1992 improve gitcontextmock#2093
Conversation
Coverage Report for CI Build 28948555099Coverage decreased (-0.02%) to 72.106%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions1 previously-covered line in 1 file lost coverage.
Coverage Stats💛 - Coveralls |
maybeec
left a comment
There was a problem hiding this comment.
Thanks for tackling this — the mock now models the full clone → fetch → pull → update-detection lifecycle with a pending-commits queue, and CI is green. Nicely structured and the new GitContextMockTest reads well.
I have two blocking items that come straight from the issue text / project conventions, plus a scope question. Inline comments give one-click suggestions for the convention fix.
Must-fix
- Use the project's
IniFilesupport for the.git/configfile. #1992 explicitly asks that the config be created "with our IniFile support". Both the write (inclone) and the read (inretrieveGitUrl) are currently hand-rolled.com.devonfw.tools.ide.io.ini.IniFile/IniFileImplalready exist — using them also removes the fragile line parsing (see inline comments). - Exception handling convention.
coding-conventions.adoc(§"Catching and handling Exceptions") requires wrapping asthrow new IllegalStateException("<meaningful message>", e)— a message and the cause. There are 8 barethrow new RuntimeException(e)sites; I've left commit-able suggestions on each.
Scope question (non-blocking, worth confirming)
The issue closes with: "Our tests using GitContextMock can then be reworked and improved… Also we can properly test features like ide upgrade or the check if update is available." This PR delivers the mock infrastructure and self-tests, but the existing consumer (UpdateCommandletTest) isn't reworked and there are no feature-level tests exercising the new capabilities. Is that intended as a follow-up, or should it land here to fully close #1992?
Minor
GitChangehas noequals/hashCode, soGitCommit.hashCode()(which delegates toList<GitChange>.hashCode()) is identity-based — fine for a single run, just not content-stable. See inline note.java.io.IOExceptionFQN inclone/pullvs. the importedIOExceptionelsewhere is inconsistent (cosmetic).- Heads-up:
IdeTestContextdefaults toGitContextMock, so flipping these methods from no-ops to active FS operations changes semantics for every consumer (e.g.updateUrls()now creates a.gittree under the urls path). CI is green so nothing breaks today — just noting the widened side-effect surface.
Also needs a rebase — the branch is currently behind main.
| StringBuilder cfg = new StringBuilder(); | ||
| cfg.append("[remote \"origin\"]\n"); | ||
| cfg.append("\turl = ").append(gitUrl.url()).append('\n'); | ||
| Files.writeString(gitFolder.resolve("config"), cfg.toString()); |
There was a problem hiding this comment.
Per #1992 this config should be produced "with our IniFile support" rather than a hand-built string. com.devonfw.tools.ide.io.ini.IniFileImpl gives you getOrCreateSection for the remote "origin" section plus setting the url property, then write it out. That keeps the format consistent with the real GitContextImpl and makes the round-trip with retrieveGitUrl symmetric.
| try { | ||
| for (String line : Files.readAllLines(cfg)) { | ||
| String trimmed = line.trim(); | ||
| if (trimmed.startsWith("url =")) { |
There was a problem hiding this comment.
Two issues here, both solved by reading via IniFile/IniFileImpl (see the clone comment):
- This returns the first line starting with
url =regardless of section, so it isn't scoped to[remote "origin"]— with multiple remotes it would pick the wrong one. - It re-implements ini parsing by hand, which Improve GitContextMock #1992 explicitly asked to avoid.
Read the origin section's url property instead, falling back to MOCKED_URL_VALUE when no config exists.
| cfg.append("\turl = ").append(gitUrl.url()).append('\n'); | ||
| Files.writeString(gitFolder.resolve("config"), cfg.toString()); | ||
| } catch (java.io.IOException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
Follow the project convention for caught exceptions. The same fix applies to every other throw new RuntimeException(e) in this file (lines 135, 158, 190, 257, 280, 313, 355) — each has its own suggestion below.
| throw new RuntimeException(e); | |
| throw new IllegalStateException("Failed to clone Git repository " + gitUrl + " to " + repository, e); |
📖 Convention: coding-conventions.adoc → "Catching and handling Exceptions" — always wrap a caught exception as IllegalStateException("<meaningful message>", e) carrying the original cause e. A bare throw new RuntimeException(e) violates this: it uses the wrong type and, without a message, discards diagnostic context.
| Files.writeString(branchRefPath, lastId); | ||
| Files.writeString(gitFolder.resolve(FILE_FETCH_HEAD), lastId); | ||
| } catch (java.io.IOException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
| throw new RuntimeException(e); | |
| throw new IllegalStateException("Failed to pull repository " + repository, e); |
📖 Convention: coding-conventions.adoc → "Catching and handling Exceptions" — always wrap a caught exception as IllegalStateException("<meaningful message>", e) carrying the original cause e. A bare throw new RuntimeException(e) violates this: it uses the wrong type and, without a message, discards diagnostic context.
| Files.createDirectories(gitFolder); | ||
| Files.writeString(gitFolder.resolve(FILE_FETCH_HEAD), String.valueOf(last.hashCode())); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
| throw new RuntimeException(e); | |
| throw new IllegalStateException("Failed to fetch repository " + repository, e); |
📖 Convention: coding-conventions.adoc → "Catching and handling Exceptions" — always wrap a caught exception as IllegalStateException("<meaningful message>", e) carrying the original cause e. A bare throw new RuntimeException(e) violates this: it uses the wrong type and, without a message, discards diagnostic context.
| } | ||
| return !f.equals(h); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
| throw new RuntimeException(e); | |
| throw new IllegalStateException("Failed to check for repository update in " + repository, e); |
📖 Convention: coding-conventions.adoc → "Catching and handling Exceptions" — always wrap a caught exception as IllegalStateException("<meaningful message>", e) carrying the original cause e. A bare throw new RuntimeException(e) violates this: it uses the wrong type and, without a message, discards diagnostic context.
| String t = Files.readString(trackedCommitIdPath).trim(); | ||
| return !f.equals(t); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
| throw new RuntimeException(e); | |
| throw new IllegalStateException("Failed to check for repository update in " + repository, e); |
📖 Convention: coding-conventions.adoc → "Catching and handling Exceptions" — always wrap a caught exception as IllegalStateException("<meaningful message>", e) carrying the original cause e. A bare throw new RuntimeException(e) violates this: it uses the wrong type and, without a message, discards diagnostic context.
| // fallback: return content | ||
| return content; | ||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
| throw new RuntimeException(e); | |
| throw new IllegalStateException("Failed to determine current branch for repository " + repository, e); |
📖 Convention: coding-conventions.adoc → "Catching and handling Exceptions" — always wrap a caught exception as IllegalStateException("<meaningful message>", e) carrying the original cause e. A bare throw new RuntimeException(e) violates this: it uses the wrong type and, without a message, discards diagnostic context.
| Files.copy(s, dest, StandardCopyOption.REPLACE_EXISTING); | ||
| } | ||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
| throw new RuntimeException(e); | |
| throw new IllegalStateException("Failed to copy directory " + source + " to " + target, e); |
📖 Convention: coding-conventions.adoc → "Catching and handling Exceptions" — always wrap a caught exception as IllegalStateException("<meaningful message>", e) carrying the original cause e. A bare throw new RuntimeException(e) violates this: it uses the wrong type and, without a message, discards diagnostic context.
| /** | ||
| * Represents a file or directory change to apply when pulling a commit. | ||
| */ | ||
| public static class GitChange { |
There was a problem hiding this comment.
Minor: GitChange overrides neither equals nor hashCode, so GitCommit.hashCode() (delegating to List<GitChange>.hashCode()) ends up identity-based. That's fine for distinguishing "before vs. after" state within one test run, but two commits with identical content get different ids and ids aren't reproducible across runs. Consider making GitChange/GitCommit records (or adding equals/hashCode) if you want content-stable ids.
maybeec
left a comment
There was a problem hiding this comment.
Requesting changes — there are blocking items that should be resolved before merge (details are in the inline comments on the previous review):
Blocking
- Use the project's
IniFilesupport for.git/config(write inclone, read inretrieveGitUrl). #1992 explicitly requires this, and it removes the fragile hand-rolled parsing that currently isn't scoped to the[remote "origin"]section. - Exception handling convention — replace the 8
throw new RuntimeException(e)sites withIllegalStateException("<message>", e)(message + cause), percoding-conventions.adoc. One-click suggestions are provided on each line.
Please also clarify (scope of #1992)
- The issue asks that the existing tests using
GitContextMockbe reworked and that features likeide upgrade/ update-detection be properly tested with the new mock. That part isn't in this PR — is it a planned follow-up, or should it land here to fully close #1992?
Once the two blocking items are addressed (and the branch rebased onto main), I'm happy to re-review. Thanks again — the core lifecycle modelling is solid.
7fe0b65 to
0d37f75
Compare
Thanks for the review. I addressed the two blocking items:
I also added equals/hashCode for GitChange and GitCommit to address the minor hash stability note. Regarding the scope question: I intended this PR to focus on the GitContextMock infrastructure and its self-tests. Reworking consumer tests such as UpdateCommandletTest would be a follow-up, unless you prefer to include it here. |
This PR fixes #1992
Implemented changes:
Testing instructions
Please add conscise, understandable instructions on how a reviewer can test/verify the functionality of your contribution here:
GitContextMockTest.Checklist for this PR
Make sure everything is checked before merging this PR. For further info please also see
our DoD.
mvn clean testlocally all tests pass and build is successful#«issue-id»: «brief summary»(e.g.#921: fixed setup.bat). If no issue ID exists, title only.In Progressand assigned to you or there is no issue (might happen for very small PRs)with
internal