SONARJAVA-6330: Implement S8692 - The system clock should not be used in tests#5611
SONARJAVA-6330: Implement S8692 - The system clock should not be used in tests#5611aurelien-coet-sonarsource wants to merge 7 commits into
Conversation
a264a6e to
add8e7f
Compare
SummaryImplements S8692: a new static analysis rule that detects direct use of the system clock in unit tests. The rule flags calls to Tests should use fixed or mocked clocks via dependency injection to remain deterministic and reproducible. The PR includes the check implementation, comprehensive unit test samples showing compliant and noncompliant patterns, rule documentation explaining the problem and solutions, and baseline expectations from real-world projects (commons-beanutils, eclipse-jetty, sonar-server). What reviewers should knowCheck implementation: SystemClockCheck.java uses MethodMatchers to detect three groups of invocations: parameterless Test coverage: SystemClockCheckSample.java demonstrates the rule in action—start here to see what triggers and what's compliant (fixed clocks, mocked clocks, nanoTime). Documentation: S8692.html provides clear rationale (test flakiness/non-determinism) and two solution patterns (fixed clocks, injected mocks). Good reference for reviewers unfamiliar with clock testing patterns. Ruling baselines: Four JSON files contain expected findings from the autoscan and ruling test suites. These are critical for integration testing—verify they align with actual scan results. Minor note: S3577 false negatives increased by 1, likely a side effect of test file scanning changes. Profile integration: Added to Sonar_way_profile.json, so it's active in the default quality gate.
|
|
|
||
| @Override | ||
| protected void onMethodInvocationFound(MethodInvocationTree mit) { | ||
| reportIssue(mit, "Replace this use of the system clock with a fixed clock."); |
There was a problem hiding this comment.
Let's keep the message generic, so it also covers reading the clock to measure elapsed time..
|
| } | ||
|
|
||
| @Test | ||
| void testSystemClockInstants() { |
There was a problem hiding this comment.
Instant.ofEpochMilli(System.currentTimeMillis()); is compliant. Should this rule catch it too? (There is also System.nanoTime())
There was a problem hiding this comment.
Yes, I think that in the first example, we should probably raise an issue on System.currentTimeMillis() (rather than Instant.ofEpochMilli(), since it doesn't have anything to do with the system clock). System.nanoTime() doesn't read the system clock and is very commonly used to measure elapsed time, so I don't think we should raise on it either.
There was a problem hiding this comment.
Even if System.nanoTime doesn't read the wall-clock system time, it still uses the hardware to measure time (which still makes the tests non deterministic), so I think it could be targeted by the rule too
There was a problem hiding this comment.
Yes, however my issue with including System.nanoTime is that it is often one of the recommended ways to measure elapsed time between two points in tests. It is not a problem to read System.nanoTime in a test because it does not depend on the system clock (which could change between runs or even during a run). As specified in the doc: "This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time".
There was a problem hiding this comment.
Ok, you're right, it seems to be out of scope for this rule
Agentic Analysis: Early ResultsAgentic Analysis and Context Augmentation are available on your project. Here are some issues that could have been prevented. Follow the links to learn how to put them into action. 15 issue(s) found across 1 file(s):
Analyzed by SonarQube Agentic Analysis in 6.1 s |
a5810bd to
acd3409
Compare
| "datetime", | ||
| "tests" | ||
| ], | ||
| "defaultSeverity": "Major", |
There was a problem hiding this comment.
We should probably update the RSPEC because the default severity doesn't match the highest impact severity
|
| long currentTimeMillis = System.currentTimeMillis(); // Noncompliant {{Do not use the system clock in tests.}} | ||
| // ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| long currentTimeNanos = System.nanoTime(); // Compliant: nanoTime is typically used to measure elapsed time in tests | ||
| assertTrue(currentTimeMillis < currentTimeNanos); |
There was a problem hiding this comment.
Here is an idea for another rule: if you use a suffix such as millis or nanos, don't mix them up :) It may not be straightfoward to implement though.
| .build(), | ||
| MethodMatchers.create() | ||
| .ofTypes("java.lang.System") | ||
| .names("currentTimeMillis") |
There was a problem hiding this comment.
It just occured to me that new java.util.Date() is another way to read the system clock and I think it is used frequently. I know that is class should not be used, and we probably have a rule for that, but this is an orthogonal concern - someone may allow using java.util.Date in their code (compatibility with 3rd party code is a good reason to do so) and still want to have an issue about reading system clock in tests.
There was a problem hiding this comment.
You're right, I'll add calls to the Date constructors to the nodes we should raise the issue on.
There was a problem hiding this comment.
I assume you reviewed all these sources.
There was a problem hiding this comment.
Yes, these are all occurrences of System.currentTimeMillis in tests !




No description provided.