Repository: SonarSource/sonar-scanner-msbuild
Pull request #2443: SCAN4NET-385 Use AnalysisContext in CppTest
Author: login: martin-strecker-sonarsource
Created: 2025-03-31T12:42:00Z | Merged: 2025-04-01T14:27:46Z

[SCAN4NET-385](https://sonarsource.atlassian.net/browse/SCAN4NET-385)



[SCAN4NET-385]: https://sonarsource.atlassian.net/browse/SCAN4NET-385?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java b/its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
index b31164c7a6..d11ffd70af 100644
--- a/its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
+++ b/its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
@@ -19,22 +19,24 @@
  */
 package com.sonar.it.scanner.msbuild.sonarqube;
 
+import com.sonar.it.scanner.msbuild.utils.AnalysisContext;
 import com.sonar.it.scanner.msbuild.utils.ContextExtension;
+import com.sonar.it.scanner.msbuild.utils.GeneralCommand;
+import com.sonar.it.scanner.msbuild.utils.TempDirectory;
 import com.sonar.it.scanner.msbuild.utils.TestUtils;
 import com.sonar.orchestrator.build.BuildResult;
 import com.sonar.orchestrator.util.ZipUtils;
 import java.io.File;
+import java.io.IOException;
 import java.net.URL;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
-import java.util.stream.Collectors;
 import org.apache.commons.io.FileUtils;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.api.io.TempDir;
 import org.sonarqube.ws.Issues.Issue;
 import com.sonar.it.scanner.msbuild.utils.QualityProfiles;
+
 import static com.sonar.it.scanner.msbuild.sonarqube.ServerTests.ORCHESTRATOR;
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -45,92 +47,86 @@
 @ExtendWith({ServerTests.class, ContextExtension.class})
 class CppTest {
 
-  @TempDir
-  public Path basePath;
-
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
-
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
-    ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
+    var context = AnalysisContext.forServer("CppSolution");
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSolution");
-    File wrapperOutDir = new File(projectDir.toFile(), "out");
+    ORCHESTRATOR.getServer().provisionProject(context.projectKey, context.projectKey);
+    ORCHESTRATOR.getServer().associateProjectToQualityProfile(context.projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
+    File wrapperOutDir = new File(context.projectDir.toFile(), "out");
 
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    var beginResult = context.begin
       .setProperty("sonar.cfamily.build-wrapper-output", wrapperOutDir.toString())
       .execute(ORCHESTRATOR);
-
-    File buildWrapperZip = new File(basePath.toString(), "build-wrapper-win-x86.zip");
-    File buildWrapperDir = basePath.toFile();
-    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
-    ZipUtils.unzip(buildWrapperZip, buildWrapperDir);
+    assertThat(beginResult.isSuccess()).describedAs("C++ begin step failed with logs %n%s", beginResult.getLogs()).isTrue();
 
     String platformToolset = System.getProperty("msbuild.platformtoolset", "v140");
     String windowsSdk = System.getProperty("msbuild.windowssdk", "10.0.18362.0");
-
-    TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, projectDir, new File(buildWrapperDir, "build-wrapper-win-x86/build-wrapper-win-x86-64.exe"),
-      wrapperOutDir, "/t:Rebuild",
-      String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
-      String.format("/p:PlatformToolset=%s", platformToolset));
-
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
-    assertThat(result.isSuccess()).isTrue();
-    assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
-
-    List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, projectKey);
-
-    List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
-    assertThat(keys).containsAll(List.of("cpp:S106"));
-
-    assertThat(TestUtils.getMeasureAsInteger(projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(15);
-    assertThat(TestUtils.getMeasureAsInteger(fileKey, "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    try (var buildWrapperDir = getBuildWrapperDir(context)) {
+      runMSBuildWithBuildWrapper(context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
+        wrapperOutDir, "/t:Rebuild",
+        String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
+        String.format("/p:PlatformToolset=%s", platformToolset));
+      BuildResult result = context.end.execute(ORCHESTRATOR);
+      assertThat(result.isSuccess()).as(result.getLogs()).isTrue();
+      assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
+
+      List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, context.projectKey);
+      assertThat(issues).extracting(Issue::getRule).containsAll(List.of("cpp:S106"));
+      assertThat(TestUtils.getMeasureAsInteger(context.projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(15);
+      assertThat(TestUtils.getMeasureAsInteger(context.projectKey + ":ConsoleApp/ConsoleApp.cpp", "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    }
   }
 
   @Test
   void testCppWithSharedFiles() throws Exception {
-    String projectKey = "cpp-shared";
-    String fileKey = "cpp-shared:Project1/Project1.cpp";
+    var context = AnalysisContext.forServer("CppSharedFiles");
 
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
-    ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
+    ORCHESTRATOR.getServer().provisionProject(context.projectKey, context.projectKey);
+    ORCHESTRATOR.getServer().associateProjectToQualityProfile(context.projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSharedFiles");
-    File wrapperOutDir = new File(projectDir.toFile(), "out");
+    File wrapperOutDir = new File(context.projectDir.toFile(), "out");
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
-
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    var beginResult = context.begin
       .setProperty("sonar.cfamily.build-wrapper-output", wrapperOutDir.toString())
       .execute(ORCHESTRATOR);
-
-    File buildWrapperZip = new File(basePath.toString(), "build-wrapper-win-x86.zip");
-    File buildWrapperDir = Files.createDirectories(basePath).toFile();
-    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
-    ZipUtils.unzip(buildWrapperZip, buildWrapperDir);
+    assertThat(beginResult.isSuccess()).describedAs("C++ begin step failed with logs %n%s", beginResult.getLogs()).isTrue();
 
     String platformToolset = System.getProperty("msbuild.platformtoolset", "v140");
     String windowsSdk = System.getProperty("msbuild.windowssdk", "10.0.18362.0");
+    try (var buildWrapperDir = getBuildWrapperDir(context);) {
+      runMSBuildWithBuildWrapper(context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
+        wrapperOutDir, "/t:Rebuild",
+        String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
+        String.format("/p:PlatformToolset=%s", platformToolset));
+
+      BuildResult result = context.end.execute(ORCHESTRATOR);
+      assertThat(result.isSuccess()).as(result.getLogs()).isTrue();
+      assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
+
+      List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, context.projectKey);
+      assertThat(issues).extracting(Issue::getRule).containsAll(List.of("cpp:S106"));
+      assertThat(TestUtils.getMeasureAsInteger(context.projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(22);
+      assertThat(TestUtils.getMeasureAsInteger(context.projectKey + ":Project1/Project1.cpp", "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    }
+  }
 
-    TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, projectDir, new File(buildWrapperDir, "build-wrapper-win-x86/build-wrapper-win-x86-64.exe"),
-      wrapperOutDir, "/t:Rebuild",
-      String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
-      String.format("/p:PlatformToolset=%s", platformToolset));
-
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
-    assertThat(result.isSuccess()).isTrue();
-    assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
-
-    List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, projectKey);
-
-    List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
-    assertThat(keys).containsAll(List.of("cpp:S106"));
+  private static TempDirectory getBuildWrapperDir(AnalysisContext context) throws IOException {
+    File buildWrapperZip = new File(context.projectDir.toString(), "build-wrapper-win-x86.zip");
+    var buildWrapperDir = new TempDirectory("cpp-build-wrapper");
+    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
+    ZipUtils.unzip(buildWrapperZip, buildWrapperDir.path.toFile());
+    return buildWrapperDir;
+  }
 
-    assertThat(TestUtils.getMeasureAsInteger(projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(22);
-    assertThat(TestUtils.getMeasureAsInteger(fileKey, "ncloc", ORCHESTRATOR)).isEqualTo(8);
+  private static void runMSBuildWithBuildWrapper(Path projectDir, File buildWrapperPath, File outDir, String... arguments) {
+    Path msBuildPath = TestUtils.getMsBuildPath(ORCHESTRATOR);
+    new GeneralCommand(buildWrapperPath.toString(), projectDir)
+      .addArgument("--out-dir")
+      .addArgument(outDir.toString())
+      .addArgument(msBuildPath.toString())
+      .addArguments(arguments)
+      .execute();
   }
 }
diff --git a/its/src/test/java/com/sonar/it/scanner/msbuild/utils/BuildCommand.java b/its/src/test/java/com/sonar/it/scanner/msbuild/utils/BuildCommand.java
index 3c4dc0412b..62f39117cd 100644
--- a/its/src/test/java/com/sonar/it/scanner/msbuild/utils/BuildCommand.java
+++ b/its/src/test/java/com/sonar/it/scanner/msbuild/utils/BuildCommand.java
@@ -69,9 +69,10 @@ public BuildCommand addArgument(String... arguments) {
   public BuildResult execute() {
     var command = createCommand();
     var result = new BuildResult();
-    LOG.info("Command line: {}", command.toCommandLine());
+    LOG.info("Build command start: '{}' in {}", command.toCommandLine(), command.getDirectory());
     result.addStatus(CommandExecutor.create().execute(command, new StreamConsumer.Pipe(result.getLogsWriter()), timeout));
     assertThat(result.isSuccess()).describedAs("BUILD step failed. Logs: " + result.getLogs()).isTrue();
+    LOG.info("Build command finish: '{}' in {}", command.toCommandLine(), command.getDirectory());
     return result;
   }
 
diff --git a/its/src/test/java/com/sonar/it/scanner/msbuild/utils/GeneralCommand.java b/its/src/test/java/com/sonar/it/scanner/msbuild/utils/GeneralCommand.java
index 036dd02766..b9138e2bd0 100644
--- a/its/src/test/java/com/sonar/it/scanner/msbuild/utils/GeneralCommand.java
+++ b/its/src/test/java/com/sonar/it/scanner/msbuild/utils/GeneralCommand.java
@@ -24,23 +24,21 @@
 import com.sonar.orchestrator.util.CommandExecutor;
 import com.sonar.orchestrator.util.StreamConsumer;
 import java.nio.file.Path;
-import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
 
 import static com.sonar.it.scanner.msbuild.utils.TestUtils.LOG;
 import static com.sonar.it.scanner.msbuild.utils.TestUtils.TIMEOUT_LIMIT;
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class GeneralCommand extends BaseCommand<GeneralCommand> {
+  private final String command;
   private long timeout = TIMEOUT_LIMIT;
-  private final Command command;
+  private final ArrayList<String> arguments = new ArrayList<>();
 
-  private GeneralCommand(String command, Path workingDirectory) {
+  public GeneralCommand(String command, Path workingDirectory) {
     super(workingDirectory);
-    this.command = Command.create(command);
-  }
-
-  public static GeneralCommand create(String command, Path workingDirectory) {
-    return new GeneralCommand(command, workingDirectory);
+    this.command = command;
   }
 
   public GeneralCommand setTimeout(long timeout) {
@@ -49,17 +47,25 @@ public GeneralCommand setTimeout(long timeout) {
   }
 
   public GeneralCommand addArgument(String arg) {
-    command.addArgument(arg);
+    arguments.add(arg);
+    return this;
+  }
+
+  public GeneralCommand addArguments(String... arguments) {
+    this.arguments.addAll(List.of(arguments));
     return this;
   }
 
   public BuildResult execute() {
+    var command = Command.create(this.command).setDirectory(projectDir.toFile()).addArguments(arguments);
+    environment.forEach(command::setEnvironmentVariable);
     var commandLine = command.toCommandLine();
-    LOG.info("Command line start: {}", commandLine);
+    LOG.info("Command line start: '{}' in {}", commandLine, command.getDirectory());
     var result = new BuildResult();
     var returnCode = CommandExecutor.create().execute(command, new StreamConsumer.Pipe(result.getLogsWriter()), timeout);
     result.addStatus(returnCode);
     assertThat(result.isSuccess()).describedAs("Command '" + commandLine + "' failed with logs: " + result.getLogs()).isTrue();
+    LOG.info("Command line finish: '{}' in {}", commandLine, command.getDirectory());
     return result;
   }
 
diff --git a/its/src/test/java/com/sonar/it/scanner/msbuild/utils/ScannerCommand.java b/its/src/test/java/com/sonar/it/scanner/msbuild/utils/ScannerCommand.java
index db8357c95d..f39b9cc9f3 100644
--- a/its/src/test/java/com/sonar/it/scanner/msbuild/utils/ScannerCommand.java
+++ b/its/src/test/java/com/sonar/it/scanner/msbuild/utils/ScannerCommand.java
@@ -108,8 +108,9 @@ public ScannerCommand setTimeout(long timeout) {
   public BuildResult execute(Orchestrator orchestrator) {
     var command = createCommand(orchestrator);
     var result = new BuildResult();
-    LOG.info("Command line: {}", command.toCommandLine());
+    LOG.info("Scanner command start: '{}' in {}", command.toCommandLine(), command.getDirectory());
     result.addStatus(CommandExecutor.create().execute(command, new StreamConsumer.Pipe(result.getLogsWriter()), timeout));
+    LOG.info("Scanner command finish: '{}' in {}", command.toCommandLine(), command.getDirectory());
     if (step == Step.end) {
       if (orchestrator == null) {
         CloudUtils.waitForTaskProcessing(result.getLogs());
diff --git a/its/src/test/java/com/sonar/it/scanner/msbuild/utils/TestUtils.java b/its/src/test/java/com/sonar/it/scanner/msbuild/utils/TestUtils.java
index fd2f4272a1..9dec9af478 100644
--- a/its/src/test/java/com/sonar/it/scanner/msbuild/utils/TestUtils.java
+++ b/its/src/test/java/com/sonar/it/scanner/msbuild/utils/TestUtils.java
@@ -153,14 +153,14 @@ public static Location getMavenLocation(String groupId, String artifactId, Strin
   public static void createVirtualDrive(String drive, Path projectDir, String subDirectory) {
     var target = projectDir.resolve(subDirectory).toAbsolutePath().toString();
     // If SUBST fails, it's most likely flakiness from a previous canceled run that did not clean up the drive.
-    GeneralCommand.create("SUBST", projectDir)
+    new GeneralCommand("SUBST", projectDir)
       .addArgument(drive)
       .addArgument(target)
       .execute();
   }
 
   public static void deleteVirtualDrive(String drive, Path projectDir) {
-    GeneralCommand.create("SUBST", projectDir)
+    new GeneralCommand("SUBST", projectDir)
       .addArgument(drive).addArgument("/D")
       .execute();
   }
@@ -179,20 +179,6 @@ public static Path projectDir(Path temp, String projectName) {
     }
   }
 
-  public static void runMSBuildWithBuildWrapper(Orchestrator orch, Path projectDir, File buildWrapperPath, File outDir,
-    String... arguments) {
-    Path msBuildPath = getMsBuildPath(orch);
-
-    int r = CommandExecutor.create().execute(
-      initCommandEnvironment(Command.create(buildWrapperPath.toString()), Collections.emptyList())
-        .addArgument("--out-dir")
-        .addArgument(outDir.toString())
-        .addArgument(msBuildPath.toString())
-        .addArguments(arguments)
-        .setDirectory(projectDir.toFile()), TIMEOUT_LIMIT);
-    assertThat(r).isZero();
-  }
-
   public static void updateSetting(Orchestrator orchestrator, String projectKey, String propertyKey, List<String> values) {
     newWsClient(orchestrator).settings().set(new SetRequest().setComponent(projectKey).setKey(propertyKey).setValues(values));
   }


Reviews:
user: login: pavel-mikula-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T13:05:45Z
user: login: pavel-mikula-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T13:06:02Z
user: login: pavel-mikula-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T13:07:10Z
user: login: pavel-mikula-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T13:15:09Z
user: login: pavel-mikula-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T13:15:54Z
user: login: pavel-mikula-sonarsource
body: Let's make `runMSBuildWithBuildWrapper` local
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T07:59:45Z
user: login: pavel-mikula-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T08:00:17Z
user: login: pavel-mikula-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T08:00:50Z
user: login: martin-strecker-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T08:10:35Z
user: login: pavel-mikula-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T11:55:04Z
user: login: pavel-mikula-sonarsource
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T13:45:50Z
user: login: martin-strecker-sonarsource
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T14:14:41Z
user: login: pavel-mikula-sonarsource
body: Still LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T14:17:50Z

Review comments:
diff_hunk: @@ -50,18 +51,19 @@ class CppTest {
 
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
+    var context = AnalysisContext.forServer("CppSolution");
+    String projectKey = context.projectKey;
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: Just usual cleanup
```suggestion
```
created_at: 2025-03-31T13:05:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,18 +51,19 @@ class CppTest {
 
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
+    var context = AnalysisContext.forServer("CppSolution");
+    String projectKey = context.projectKey;
+    String fileKey = projectKey + ":ConsoleApp/ConsoleApp.cpp";
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: Also used once=> not worth a name
```suggestion
```
created_at: 2025-03-31T13:06:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -50,18 +51,19 @@ class CppTest {
 
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
+    var context = AnalysisContext.forServer("CppSolution");
+    String projectKey = context.projectKey;
+    String fileKey = projectKey + ":ConsoleApp/ConsoleApp.cpp";
 
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
+    ORCHESTRATOR.getServer().provisionProject(projectKey, projectKey);
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: Just FYI, no changes here (yet, unless there will be a rebase conflict)

I already have a PR in progress to have `context.setQualityProfile(QualityProfile.CPP_S106);`
created_at: 2025-03-31T13:07:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -77,8 +73,7 @@ void testCppOnly() throws Exception {
       wrapperOutDir, "/t:Rebuild",
       String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
       String.format("/p:PlatformToolset=%s", platformToolset));
-
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
+    BuildResult result = context.end.execute(ORCHESTRATOR);
     assertThat(result.isSuccess()).isTrue();
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: This assert is needed. Adding the `.describeAs(result.getLogs())` will make it more helpful when it breaks
created_at: 2025-03-31T13:15:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,28 +45,24 @@
 @ExtendWith({ServerTests.class, ContextExtension.class})
 class CppTest {
 
-  @TempDir
-  public Path basePath;
-
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
+    var context = AnalysisContext.forServer("CppSolution");
+    String projectKey = context.projectKey;
+    String fileKey = projectKey + ":ConsoleApp/ConsoleApp.cpp";
 
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
+    ORCHESTRATOR.getServer().provisionProject(projectKey, projectKey);
     ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSolution");
+    Path projectDir = context.projectDir;
     File wrapperOutDir = new File(projectDir.toFile(), "out");
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
-
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    context.begin
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: The result of this execute call should be asserted to be successful. And adding the `.describeAs` will make it also more useful.
created_at: 2025-03-31T13:15:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,92 +45,82 @@
 @ExtendWith({ServerTests.class, ContextExtension.class})
 class CppTest {
 
-  @TempDir
-  public Path basePath;
-
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
-
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
-    ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
+    var context = AnalysisContext.forServer("CppSolution");
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSolution");
-    File wrapperOutDir = new File(projectDir.toFile(), "out");
+    ORCHESTRATOR.getServer().provisionProject(context.projectKey, context.projectKey);
+    ORCHESTRATOR.getServer().associateProjectToQualityProfile(context.projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
+    File wrapperOutDir = new File(context.projectDir.toFile(), "out");
 
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    var beginResult = context.begin
       .setProperty("sonar.cfamily.build-wrapper-output", wrapperOutDir.toString())
       .execute(ORCHESTRATOR);
-
-    File buildWrapperZip = new File(basePath.toString(), "build-wrapper-win-x86.zip");
-    File buildWrapperDir = basePath.toFile();
-    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
-    ZipUtils.unzip(buildWrapperZip, buildWrapperDir);
+    assertThat(beginResult.isSuccess()).describedAs("C++ begin step failed with logs %n%s", beginResult.getLogs()). isTrue();
 
     String platformToolset = System.getProperty("msbuild.platformtoolset", "v140");
     String windowsSdk = System.getProperty("msbuild.windowssdk", "10.0.18362.0");
-
-    TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, projectDir, new File(buildWrapperDir, "build-wrapper-win-x86/build-wrapper-win-x86-64.exe"),
-      wrapperOutDir, "/t:Rebuild",
-      String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
-      String.format("/p:PlatformToolset=%s", platformToolset));
-
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
-    assertThat(result.isSuccess()).isTrue();
-    assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
-
-    List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, projectKey);
-
-    List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
-    assertThat(keys).containsAll(List.of("cpp:S106"));
-
-    assertThat(TestUtils.getMeasureAsInteger(projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(15);
-    assertThat(TestUtils.getMeasureAsInteger(fileKey, "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    try (var buildWrapperDir = getBuildWrapperDir(context)) {
+      TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
+        wrapperOutDir, "/t:Rebuild",
+        String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
+        String.format("/p:PlatformToolset=%s", platformToolset));
+      BuildResult result = context.end.execute(ORCHESTRATOR);
+      assertThat(result.isSuccess()).as(result.getLogs()).isTrue();
+      assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
+
+      List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, context.projectKey);
+
+      List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: Optional: This is slightly less cumbersome with the `.extracting` trick
created_at: 2025-04-01T07:56:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,92 +45,82 @@
 @ExtendWith({ServerTests.class, ContextExtension.class})
 class CppTest {
 
-  @TempDir
-  public Path basePath;
-
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
-
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
-    ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
+    var context = AnalysisContext.forServer("CppSolution");
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSolution");
-    File wrapperOutDir = new File(projectDir.toFile(), "out");
+    ORCHESTRATOR.getServer().provisionProject(context.projectKey, context.projectKey);
+    ORCHESTRATOR.getServer().associateProjectToQualityProfile(context.projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
+    File wrapperOutDir = new File(context.projectDir.toFile(), "out");
 
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    var beginResult = context.begin
       .setProperty("sonar.cfamily.build-wrapper-output", wrapperOutDir.toString())
       .execute(ORCHESTRATOR);
-
-    File buildWrapperZip = new File(basePath.toString(), "build-wrapper-win-x86.zip");
-    File buildWrapperDir = basePath.toFile();
-    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
-    ZipUtils.unzip(buildWrapperZip, buildWrapperDir);
+    assertThat(beginResult.isSuccess()).describedAs("C++ begin step failed with logs %n%s", beginResult.getLogs()). isTrue();
 
     String platformToolset = System.getProperty("msbuild.platformtoolset", "v140");
     String windowsSdk = System.getProperty("msbuild.windowssdk", "10.0.18362.0");
-
-    TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, projectDir, new File(buildWrapperDir, "build-wrapper-win-x86/build-wrapper-win-x86-64.exe"),
-      wrapperOutDir, "/t:Rebuild",
-      String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
-      String.format("/p:PlatformToolset=%s", platformToolset));
-
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
-    assertThat(result.isSuccess()).isTrue();
-    assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
-
-    List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, projectKey);
-
-    List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
-    assertThat(keys).containsAll(List.of("cpp:S106"));
-
-    assertThat(TestUtils.getMeasureAsInteger(projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(15);
-    assertThat(TestUtils.getMeasureAsInteger(fileKey, "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    try (var buildWrapperDir = getBuildWrapperDir(context)) {
+      TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: Please remove this one from TestUtils and move it here, as a private method. It will never be reused anywhere else than in this CPP
```suggestion
      runMSBuildWithBuildWrapper(ORCHESTRATOR, context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
```
created_at: 2025-04-01T07:57:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,92 +45,82 @@
 @ExtendWith({ServerTests.class, ContextExtension.class})
 class CppTest {
 
-  @TempDir
-  public Path basePath;
-
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
-
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
-    ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
+    var context = AnalysisContext.forServer("CppSolution");
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSolution");
-    File wrapperOutDir = new File(projectDir.toFile(), "out");
+    ORCHESTRATOR.getServer().provisionProject(context.projectKey, context.projectKey);
+    ORCHESTRATOR.getServer().associateProjectToQualityProfile(context.projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
+    File wrapperOutDir = new File(context.projectDir.toFile(), "out");
 
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    var beginResult = context.begin
       .setProperty("sonar.cfamily.build-wrapper-output", wrapperOutDir.toString())
       .execute(ORCHESTRATOR);
-
-    File buildWrapperZip = new File(basePath.toString(), "build-wrapper-win-x86.zip");
-    File buildWrapperDir = basePath.toFile();
-    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
-    ZipUtils.unzip(buildWrapperZip, buildWrapperDir);
+    assertThat(beginResult.isSuccess()).describedAs("C++ begin step failed with logs %n%s", beginResult.getLogs()). isTrue();
 
     String platformToolset = System.getProperty("msbuild.platformtoolset", "v140");
     String windowsSdk = System.getProperty("msbuild.windowssdk", "10.0.18362.0");
-
-    TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, projectDir, new File(buildWrapperDir, "build-wrapper-win-x86/build-wrapper-win-x86-64.exe"),
-      wrapperOutDir, "/t:Rebuild",
-      String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
-      String.format("/p:PlatformToolset=%s", platformToolset));
-
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
-    assertThat(result.isSuccess()).isTrue();
-    assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
-
-    List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, projectKey);
-
-    List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
-    assertThat(keys).containsAll(List.of("cpp:S106"));
-
-    assertThat(TestUtils.getMeasureAsInteger(projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(15);
-    assertThat(TestUtils.getMeasureAsInteger(fileKey, "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    try (var buildWrapperDir = getBuildWrapperDir(context)) {
+      TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
+        wrapperOutDir, "/t:Rebuild",
+        String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
+        String.format("/p:PlatformToolset=%s", platformToolset));
+      BuildResult result = context.end.execute(ORCHESTRATOR);
+      assertThat(result.isSuccess()).as(result.getLogs()).isTrue();
+      assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
+
+      List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, context.projectKey);
+
+      List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
+      assertThat(keys).containsAll(List.of("cpp:S106"));
+
+      assertThat(TestUtils.getMeasureAsInteger(context.projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(15);
+      assertThat(TestUtils.getMeasureAsInteger(context.projectKey + ":ConsoleApp/ConsoleApp.cpp", "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    }
   }
 
   @Test
   void testCppWithSharedFiles() throws Exception {
-    String projectKey = "cpp-shared";
-    String fileKey = "cpp-shared:Project1/Project1.cpp";
+    var context = AnalysisContext.forServer("CppSharedFiles");
 
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
-    ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
+    ORCHESTRATOR.getServer().provisionProject(context.projectKey, context.projectKey);
+    ORCHESTRATOR.getServer().associateProjectToQualityProfile(context.projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSharedFiles");
-    File wrapperOutDir = new File(projectDir.toFile(), "out");
+    File wrapperOutDir = new File(context.projectDir.toFile(), "out");
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
-
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    var beginResult =context.begin
       .setProperty("sonar.cfamily.build-wrapper-output", wrapperOutDir.toString())
       .execute(ORCHESTRATOR);
-
-    File buildWrapperZip = new File(basePath.toString(), "build-wrapper-win-x86.zip");
-    File buildWrapperDir = Files.createDirectories(basePath).toFile();
-    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
-    ZipUtils.unzip(buildWrapperZip, buildWrapperDir);
+    assertThat(beginResult.isSuccess()).describedAs("C++ begin step failed with logs %n%s", beginResult.getLogs()). isTrue();
 
     String platformToolset = System.getProperty("msbuild.platformtoolset", "v140");
     String windowsSdk = System.getProperty("msbuild.windowssdk", "10.0.18362.0");
+    try(var buildWrapperDir = getBuildWrapperDir(context);) {
+      TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
+        wrapperOutDir, "/t:Rebuild",
+        String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
+        String.format("/p:PlatformToolset=%s", platformToolset));
 
-    TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, projectDir, new File(buildWrapperDir, "build-wrapper-win-x86/build-wrapper-win-x86-64.exe"),
-      wrapperOutDir, "/t:Rebuild",
-      String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
-      String.format("/p:PlatformToolset=%s", platformToolset));
+      BuildResult result = context.end.execute(ORCHESTRATOR);
+      assertThat(result.isSuccess()).as(result.getLogs()).isTrue();
+      assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
 
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
-    assertThat(result.isSuccess()).isTrue();
-    assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
+      List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, context.projectKey);
 
-    List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, projectKey);
+      List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
+      assertThat(keys).containsAll(List.of("cpp:S106"));
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: Optional: Same here
created_at: 2025-04-01T08:00:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,92 +45,82 @@
 @ExtendWith({ServerTests.class, ContextExtension.class})
 class CppTest {
 
-  @TempDir
-  public Path basePath;
-
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
-
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
-    ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
+    var context = AnalysisContext.forServer("CppSolution");
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSolution");
-    File wrapperOutDir = new File(projectDir.toFile(), "out");
+    ORCHESTRATOR.getServer().provisionProject(context.projectKey, context.projectKey);
+    ORCHESTRATOR.getServer().associateProjectToQualityProfile(context.projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
+    File wrapperOutDir = new File(context.projectDir.toFile(), "out");
 
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    var beginResult = context.begin
       .setProperty("sonar.cfamily.build-wrapper-output", wrapperOutDir.toString())
       .execute(ORCHESTRATOR);
-
-    File buildWrapperZip = new File(basePath.toString(), "build-wrapper-win-x86.zip");
-    File buildWrapperDir = basePath.toFile();
-    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
-    ZipUtils.unzip(buildWrapperZip, buildWrapperDir);
+    assertThat(beginResult.isSuccess()).describedAs("C++ begin step failed with logs %n%s", beginResult.getLogs()). isTrue();
 
     String platformToolset = System.getProperty("msbuild.platformtoolset", "v140");
     String windowsSdk = System.getProperty("msbuild.windowssdk", "10.0.18362.0");
-
-    TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, projectDir, new File(buildWrapperDir, "build-wrapper-win-x86/build-wrapper-win-x86-64.exe"),
-      wrapperOutDir, "/t:Rebuild",
-      String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
-      String.format("/p:PlatformToolset=%s", platformToolset));
-
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
-    assertThat(result.isSuccess()).isTrue();
-    assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
-
-    List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, projectKey);
-
-    List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
-    assertThat(keys).containsAll(List.of("cpp:S106"));
-
-    assertThat(TestUtils.getMeasureAsInteger(projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(15);
-    assertThat(TestUtils.getMeasureAsInteger(fileKey, "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    try (var buildWrapperDir = getBuildWrapperDir(context)) {
+      TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
+        wrapperOutDir, "/t:Rebuild",
+        String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
+        String.format("/p:PlatformToolset=%s", platformToolset));
+      BuildResult result = context.end.execute(ORCHESTRATOR);
+      assertThat(result.isSuccess()).as(result.getLogs()).isTrue();
+      assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
+
+      List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, context.projectKey);
+
+      List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: pavel-mikula-sonarsource
body: Then this `keys` and assertion can be a single line. And we could get rid of the empty lines before and after
created_at: 2025-04-01T08:00:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -45,92 +45,82 @@
 @ExtendWith({ServerTests.class, ContextExtension.class})
 class CppTest {
 
-  @TempDir
-  public Path basePath;
-
   @Test
   void testCppOnly() throws Exception {
-    String projectKey = "cpp";
-    String fileKey = "cpp:ConsoleApp/ConsoleApp.cpp";
-
-    ORCHESTRATOR.getServer().provisionProject(projectKey, "Cpp");
-    ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cpp", QualityProfiles.CPP_S106);
+    var context = AnalysisContext.forServer("CppSolution");
 
-    Path projectDir = TestUtils.projectDir(basePath, "CppSolution");
-    File wrapperOutDir = new File(projectDir.toFile(), "out");
+    ORCHESTRATOR.getServer().provisionProject(context.projectKey, context.projectKey);
+    ORCHESTRATOR.getServer().associateProjectToQualityProfile(context.projectKey, "cpp", QualityProfiles.CPP_S106);
 
-    String token = TestUtils.getNewToken(ORCHESTRATOR);
+    File wrapperOutDir = new File(context.projectDir.toFile(), "out");
 
-    TestUtils.newScannerBegin(ORCHESTRATOR, projectKey, projectDir, token)
+    var beginResult = context.begin
       .setProperty("sonar.cfamily.build-wrapper-output", wrapperOutDir.toString())
       .execute(ORCHESTRATOR);
-
-    File buildWrapperZip = new File(basePath.toString(), "build-wrapper-win-x86.zip");
-    File buildWrapperDir = basePath.toFile();
-    FileUtils.copyURLToFile(new URL(ORCHESTRATOR.getServer().getUrl() + "/static/cpp/build-wrapper-win-x86.zip"), buildWrapperZip);
-    ZipUtils.unzip(buildWrapperZip, buildWrapperDir);
+    assertThat(beginResult.isSuccess()).describedAs("C++ begin step failed with logs %n%s", beginResult.getLogs()). isTrue();
 
     String platformToolset = System.getProperty("msbuild.platformtoolset", "v140");
     String windowsSdk = System.getProperty("msbuild.windowssdk", "10.0.18362.0");
-
-    TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, projectDir, new File(buildWrapperDir, "build-wrapper-win-x86/build-wrapper-win-x86-64.exe"),
-      wrapperOutDir, "/t:Rebuild",
-      String.format("/p:WindowsTargetPlatformVersion=%s", windowsSdk),
-      String.format("/p:PlatformToolset=%s", platformToolset));
-
-    BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);
-    assertThat(result.isSuccess()).isTrue();
-    assertThat(result.getLogs()).doesNotContain("Invalid character encountered in file");
-
-    List<Issue> issues = TestUtils.projectIssues(ORCHESTRATOR, projectKey);
-
-    List<String> keys = issues.stream().map(Issue::getRule).collect(Collectors.toList());
-    assertThat(keys).containsAll(List.of("cpp:S106"));
-
-    assertThat(TestUtils.getMeasureAsInteger(projectKey, "ncloc", ORCHESTRATOR)).isEqualTo(15);
-    assertThat(TestUtils.getMeasureAsInteger(fileKey, "ncloc", ORCHESTRATOR)).isEqualTo(8);
+    try (var buildWrapperDir = getBuildWrapperDir(context)) {
+      TestUtils.runMSBuildWithBuildWrapper(ORCHESTRATOR, context.projectDir, buildWrapperDir.path.resolve("build-wrapper-win-x86/build-wrapper-win-x86-64.exe").toFile(),
path: its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/CppTest.java
user: login: martin-strecker-sonarsource
body: This depends on GeneralCommand introduced in #2440
created_at: 2025-04-01T08:10:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -69,9 +69,10 @@ public BuildCommand addArgument(String... arguments) {
   public BuildResult execute() {
     var command = createCommand();
     var result = new BuildResult();
-    LOG.info("Command line: {}", command.toCommandLine());
+    LOG.info("Build command start: '{}' in {}", command.toCommandLine(), command.getDirectory());
     result.addStatus(CommandExecutor.create().execute(command, new StreamConsumer.Pipe(result.getLogsWriter()), timeout));
     assertThat(result.isSuccess()).describedAs("BUILD step failed. Logs: " + result.getLogs()).isTrue();
+    LOG.info("Build command finish: '{}' Output: {}", command.toCommandLine(), result.getLogs());
path: its/src/test/java/com/sonar/it/scanner/msbuild/utils/BuildCommand.java
user: login: pavel-mikula-sonarsource
body: This should not be needed. The `execute` with the stream consumer and result already does the logging.

If it this fails, the `.describeAs` should be able to dump the logs in one piece.
created_at: 2025-04-01T11:55:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -69,9 +69,10 @@ public BuildCommand addArgument(String... arguments) {
   public BuildResult execute() {
     var command = createCommand();
     var result = new BuildResult();
-    LOG.info("Command line: {}", command.toCommandLine());
+    LOG.info("Build command start: '{}' in {}", command.toCommandLine(), command.getDirectory());
path: its/src/test/java/com/sonar/it/scanner/msbuild/utils/BuildCommand.java
user: login: martin-strecker-sonarsource
body: Without the executing directory, most commands can not be tracked down to the test method that was executing them.
created_at: 2025-04-01T14:11:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-04-01T14:05:05Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=sonarscanner-msbuild&pullRequest=2443) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=sonarscanner-msbuild&pullRequest=2443&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=sonarscanner-msbuild&pullRequest=2443&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=sonarscanner-msbuild&pullRequest=2443&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=sonarscanner-msbuild&pullRequest=2443&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=sonarscanner-msbuild&pullRequest=2443&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=sonarscanner-msbuild&pullRequest=2443)


author_association: NONE