Skip to content

Commit

Permalink
Add example implementation of ide-starter
Browse files Browse the repository at this point in the history
  • Loading branch information
asodja committed Mar 22, 2024
1 parent a88ac15 commit 5ec06de
Show file tree
Hide file tree
Showing 15 changed files with 169 additions and 23 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Expand Up @@ -7,7 +7,7 @@ testAndroidSdkVersion = "7.3.0"

[libraries]
commonIo = "commons-io:commons-io:2.11.0"
ideStarter = "com.jetbrains.intellij.tools:ide-starter-squashed:233.14475.28"
ideStarter = "com.jetbrains.intellij.tools:ide-starter-squashed:233.15026.9"
toolingApi = "org.gradle:gradle-tooling-api:7.2"

spock-core = { module = "org.spockframework:spock-core", version.ref = "spock" }
Expand Down
Expand Up @@ -2,16 +2,9 @@

import org.gradle.profiler.BuildAction;
import org.gradle.profiler.GradleClient;
import org.gradle.profiler.ide.RunIde;
import org.gradle.profiler.instrument.GradleInstrumentation;
import org.gradle.profiler.result.BuildActionResult;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.List;
import java.util.ServiceLoader;

/**
* A mock-up of Android studio sync.
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/org/gradle/profiler/studio/StudioGradleClient.java
Expand Up @@ -5,6 +5,8 @@
import org.gradle.profiler.InvocationSettings;
import org.gradle.profiler.client.protocol.ServerConnection;
import org.gradle.profiler.client.protocol.messages.*;
import org.gradle.profiler.ide.RunIdeContext;
import org.gradle.profiler.ide.RunIdeStarter;
import org.gradle.profiler.instrument.GradleInstrumentation;
import org.gradle.profiler.result.BuildActionResult;
import org.gradle.profiler.studio.invoker.StudioBuildActionResult;
Expand All @@ -16,12 +18,15 @@
import org.gradle.profiler.studio.tools.StudioSandboxCreator.StudioSandbox;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -32,6 +37,8 @@

public class StudioGradleClient implements GradleClient {



public enum CleanCacheMode {
BEFORE_SCENARIO,
BEFORE_BUILD,
Expand All @@ -48,11 +55,13 @@ public enum CleanCacheMode {
private final CleanCacheMode cleanCacheMode;
private final ExecutorService executor;
private final StudioSandbox sandbox;
private final InvocationSettings invocationSettings;
private boolean isFirstRun;

public StudioGradleClient(StudioGradleBuildConfiguration buildConfiguration, InvocationSettings invocationSettings, CleanCacheMode cleanCacheMode) {
this.isFirstRun = true;
this.cleanCacheMode = cleanCacheMode;
this.invocationSettings = invocationSettings;
Path studioInstallDir = invocationSettings.getStudioInstallDir().toPath();
Optional<File> studioSandboxDir = invocationSettings.getStudioSandboxDir();
this.sandbox = StudioSandboxCreator.createSandbox(studioSandboxDir.map(File::toPath).orElse(null));
Expand All @@ -64,7 +73,22 @@ public StudioGradleClient(StudioGradleBuildConfiguration buildConfiguration, Inv
this.executor = Executors.newSingleThreadExecutor();
}

private RunIdeContext newIdeContext() {
URL[] classpath = GradleInstrumentation.getClasspath("ide-provisioning");
RunIdeStarter ideStarter = ServiceLoader.load(RunIdeStarter.class, new URLClassLoader(classpath, RunIdeStarter.class.getClassLoader())).iterator().next();
return ideStarter.newContext(
invocationSettings.getProjectDir().getAbsolutePath(),
invocationSettings.getStudioInstallDir().getAbsolutePath()
);
}

public BuildActionResult sync(List<String> gradleArgs, List<String> jvmArgs) {
newIdeContext()
.withCommands()
.waitForSmartMode()
.importGradleProject()
.exitApp();

if (shouldCleanCache()) {
processController.runAndWaitToStop((connections) -> {
System.out.println("* Cleaning Android Studio cache, this will require a restart...");
Expand Down
1 change: 0 additions & 1 deletion subprojects/ide-provisioning-api/build.gradle.kts
@@ -1,6 +1,5 @@
plugins {
id("profiler.embedded-library")
id("profiler.publication")
}

description = "Api for IDE provisioning capabilities for Gradle profiler"
Expand Down
@@ -0,0 +1,8 @@
package org.gradle.profiler.ide;

public interface CommandChain {
CommandChain importGradleProject();
CommandChain waitForSmartMode();
CommandChain exitApp();
void run();
}

This file was deleted.

@@ -0,0 +1,6 @@
package org.gradle.profiler.ide;

public interface RunIdeContext {
RunIdeContext withSystemProperty(String key, String value);
CommandChain withCommands();
}
@@ -0,0 +1,5 @@
package org.gradle.profiler.ide;

public interface RunIdeStarter {
RunIdeContext newContext(String projectLocation, String ideLocation);
}
1 change: 1 addition & 0 deletions subprojects/ide-provisioning/build.gradle.kts
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation(libs.ideStarter) {
exclude(group = "io.ktor")
}
implementation("org.kodein.di:kodein-di-jvm:7.20.2")
testImplementation(libs.bundles.testDependencies)
testImplementation(libs.commonIo)
}

This file was deleted.

@@ -0,0 +1,30 @@
package org.gradle.profiler.ide

import com.intellij.ide.starter.ide.IDETestContext
import com.intellij.tools.ide.performanceTesting.commands.exitApp
import com.intellij.tools.ide.performanceTesting.commands.importGradleProject
import com.intellij.tools.ide.performanceTesting.commands.waitForSmartMode

class CommandChainImpl(private val context: IDETestContext) : CommandChain {

private var commandChain = com.intellij.tools.ide.performanceTesting.commands.CommandChain()

override fun importGradleProject(): CommandChain {
commandChain = commandChain.importGradleProject()
return this
}

override fun waitForSmartMode(): CommandChain {
commandChain = commandChain.waitForSmartMode()
return this
}

override fun exitApp(): CommandChain {
commandChain = commandChain.exitApp()
return this
}

override fun run() {
context.runIDE(commands = commandChain)
}
}
@@ -0,0 +1,16 @@
package org.gradle.profiler.ide

import com.intellij.ide.starter.ide.IDETestContext

class RunIdeContextImpl(private val context: IDETestContext) : RunIdeContext {
override fun withSystemProperty(key: String, value: String): RunIdeContext {
context.applyVMOptionsPatch {
addSystemProperty(key, value)
}
return this
}

override fun withCommands(): CommandChain {
return CommandChainImpl(context)
}
}
@@ -0,0 +1,77 @@
package org.gradle.profiler.ide

import com.intellij.ide.starter.ide.IDETestContext
import com.intellij.ide.starter.ide.IdeDistributionFactory
import com.intellij.ide.starter.ide.IdeInstaller
import com.intellij.ide.starter.ide.InstalledIde
import com.intellij.ide.starter.ide.installer.ExistingIdeInstaller
import com.intellij.ide.starter.ide.installer.IdeInstallerFile
import com.intellij.ide.starter.models.IdeInfo
import com.intellij.ide.starter.models.TestCase
import com.intellij.ide.starter.path.GlobalPaths
import com.intellij.ide.starter.process.exec.ProcessExecutor
import com.intellij.ide.starter.project.LocalProjectInfo
import com.intellij.ide.starter.runner.TestContainer
import com.intellij.ide.starter.runner.TestContainerImpl
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.FileUtil
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.div
import kotlin.io.path.name
import kotlin.time.Duration.Companion.minutes

class RunIdeStarterImpl : RunIdeStarter {
override fun newContext(projectLocation: String, ideLocation: String): RunIdeContext {
val testVersion = "2023.2.3"
val ideInfo = IdeInfo(
productCode = "IC",
version = "2023.2",
// buildNumber = testVersion,
executableFileName = "idea",
fullName = "IntelliJ IDEA Community",
platformPrefix = "idea",
// getInstaller = { _ -> ExistingIdeInstaller(Paths.get(ideLocation)) }
)
val testCase = TestCase(
ideInfo,
LocalProjectInfo(Paths.get(projectLocation)),
)
val context = Starter.newContext("test", testCase)
return RunIdeContextImpl(context)
}

class ExistingIdeInstaller(private val installedIdePath: Path) : IdeInstaller {
override fun install(ideInfo: IdeInfo, includeRuntimeModuleRepository: Boolean): Pair<String, InstalledIde> {
val ideInstaller = IdeInstallerFile(installedIdePath, "locally-installed-ide")
val installDir = GlobalPaths.instance
.getCacheDirectoryFor("builds") / "${ideInfo.productCode}-${ideInstaller.buildNumber}"
installDir.toFile().deleteRecursively()
val installedIde = installedIdePath.toFile()
val destDir = installDir.resolve(installedIdePath.name).toFile()
if (SystemInfo.isMac) {
ProcessExecutor("copy app", null, 5.minutes, emptyMap(), listOf("ditto", installedIde.absolutePath, destDir.absolutePath)).start()
}
else {
FileUtil.copyDir(installedIde, destDir)
}
return Pair(
ideInstaller.buildNumber,
IdeDistributionFactory.installIDE(installDir.toFile(), ideInfo.executableFileName)
)
}
}

private object Starter {
private fun newTestContainer(): TestContainer<*> {
return TestContainerImpl()
}

fun newContext(testName: String, testCase: TestCase<*>, preserveSystemDir: Boolean = false): IDETestContext =
newTestContainer().initializeTestContext(
testName = testName,
testCase = testCase,
preserveSystemDir = preserveSystemDir
)
}
}

This file was deleted.

@@ -0,0 +1 @@
org.gradle.profiler.ide.RunIdeStarterImpl

0 comments on commit 5ec06de

Please sign in to comment.