Skip to content

Commit e839c47

Browse files
SLCORE-1077 Cancel analysis when requested by the client
1 parent 5765df6 commit e839c47

File tree

4 files changed

+135
-3
lines changed

4 files changed

+135
-3
lines changed

backend/core/src/main/java/org/sonarsource/sonarlint/core/analysis/AnalysisService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -693,8 +693,8 @@ public UUID scheduleForcedAnalysis(String configurationScopeId, List<URI> files,
693693
}
694694

695695
public CompletableFuture<AnalysisResult> scheduleAnalysis(String configurationScopeId, UUID analysisId, List<URI> files, Map<String, String> extraProperties,
696-
long startTime, boolean shouldFetchServerIssues, TriggerType triggerType) {
697-
var progressMonitor = new RpcProgressMonitor(client, new SonarLintCancelMonitor(), configurationScopeId, analysisId);
696+
long startTime, boolean shouldFetchServerIssues, TriggerType triggerType, SonarLintCancelMonitor cancelChecker) {
697+
var progressMonitor = new RpcProgressMonitor(client, cancelChecker, configurationScopeId, analysisId);
698698
var ruleDetailsCache = new ConcurrentHashMap<String, RuleDetailsForAnalysis>();
699699
var rawIssues = new ArrayList<RawIssue>();
700700
var analysisTask = new AnalyzeCommand(configurationScopeId, () -> getAnalysisConfigForEngine(configurationScopeId, files, extraProperties, false, triggerType),

backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/AnalysisRpcServiceDelegate.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public CompletableFuture<AnalyzeFilesResponse> analyzeFilesAndTrack(AnalyzeFiles
141141
var analysisResults = getBean(AnalysisService.class)
142142
.scheduleAnalysis(params.getConfigurationScopeId(), params.getAnalysisId(), params.getFilesToAnalyze(), params.getExtraProperties(), params.getStartTime(),
143143
// consider this method as an automatic analysis. This will take exclusions into account
144-
params.isShouldFetchServerIssues(), TriggerType.AUTO)
144+
params.isShouldFetchServerIssues(), TriggerType.AUTO, cancelChecker)
145145
.join();
146146
return generateAnalyzeFilesResponse(analysisResults);
147147
}, configurationScopeId);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* SonarLint Core - Medium Tests
3+
* Copyright (C) 2016-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package mediumtest.analysis;
21+
22+
import java.nio.file.Path;
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.UUID;
26+
import java.util.concurrent.TimeUnit;
27+
import mediumtest.analysis.sensor.WaitingCancellationSensor;
28+
import org.junit.jupiter.api.io.TempDir;
29+
import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams;
30+
import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto;
31+
import org.sonarsource.sonarlint.core.rpc.protocol.common.Language;
32+
import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest;
33+
import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTestHarness;
34+
35+
import static mediumtest.analysis.sensor.WaitingCancellationSensor.CANCELLATION_FILE_PATH_PROPERTY_NAME;
36+
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.awaitility.Awaitility.await;
38+
import static org.sonarsource.sonarlint.core.test.utils.plugins.SonarPluginBuilder.newSonarPlugin;
39+
import static utils.AnalysisUtils.createFile;
40+
41+
class AnalysisCancellationMediumTests {
42+
43+
private static final String CONFIG_SCOPE_ID = "CONFIG_SCOPE_ID";
44+
45+
@SonarLintTest
46+
void it_should_analyze_file_on_open(SonarLintTestHarness harness, @TempDir Path baseDir) throws InterruptedException {
47+
var filePath = createFile(baseDir, "pom.xml", "");
48+
var fileUri = filePath.toUri();
49+
var client = harness.newFakeClient()
50+
.withInitialFs(CONFIG_SCOPE_ID, baseDir, List.of(new ClientFileDto(fileUri, baseDir.relativize(filePath), CONFIG_SCOPE_ID, false,
51+
null, filePath, null, null, true)))
52+
.build();
53+
var plugin = newSonarPlugin("xml")
54+
.withSensor(WaitingCancellationSensor.class)
55+
.generate(baseDir);
56+
var backend = harness.newBackend()
57+
.withUnboundConfigScope(CONFIG_SCOPE_ID)
58+
.withStandaloneEmbeddedPlugin(plugin)
59+
.withEnabledLanguageInStandaloneMode(Language.XML)
60+
.start(client);
61+
var cancelationFilePath = baseDir.resolve("cancellation.result");
62+
var future = backend.getAnalysisService()
63+
.analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, UUID.randomUUID(), List.of(fileUri),
64+
Map.of(CANCELLATION_FILE_PATH_PROPERTY_NAME, cancelationFilePath.toString()), false, System.currentTimeMillis()));
65+
Thread.sleep(500);
66+
67+
future.cancel(false);
68+
69+
assertThat(future.isCancelled()).isTrue();
70+
await().atMost(3, TimeUnit.SECONDS)
71+
.untilAsserted(() -> assertThat(cancelationFilePath).hasContent("CANCELED"));
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* SonarLint Core - Medium Tests
3+
* Copyright (C) 2016-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package mediumtest.analysis.sensor;
21+
22+
import java.io.IOException;
23+
import java.nio.file.Files;
24+
import java.nio.file.Path;
25+
import org.sonar.api.batch.sensor.Sensor;
26+
import org.sonar.api.batch.sensor.SensorContext;
27+
import org.sonar.api.batch.sensor.SensorDescriptor;
28+
29+
public class WaitingCancellationSensor implements Sensor {
30+
31+
public static final String CANCELLATION_FILE_PATH_PROPERTY_NAME = "cancellation.file.path";
32+
33+
@Override
34+
public void describe(SensorDescriptor sensorDescriptor) {
35+
sensorDescriptor.name("WaitingCancellationSensor");
36+
}
37+
38+
@Override
39+
public void execute(SensorContext sensorContext) {
40+
var cancellationFilePath = Path.of(sensorContext.config().get(CANCELLATION_FILE_PATH_PROPERTY_NAME)
41+
.orElseThrow(() -> new IllegalArgumentException("Missing '" + CANCELLATION_FILE_PATH_PROPERTY_NAME + "' property")));
42+
var startTime = System.currentTimeMillis();
43+
while (!sensorContext.isCancelled() && startTime + 2000 > System.currentTimeMillis()) {
44+
System.out.println("HElloooo");
45+
try {
46+
Thread.sleep(200);
47+
} catch (InterruptedException e) {
48+
throw new RuntimeException(e);
49+
}
50+
}
51+
if (sensorContext.isCancelled()) {
52+
try {
53+
Files.writeString(cancellationFilePath, "CANCELED");
54+
} catch (IOException e) {
55+
throw new RuntimeException(e);
56+
}
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)