org.gradle.integtests.tooling.fixture.ToolingApi.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.integtests.tooling.fixture
import org.gradle.integtests.fixtures.daemon.DaemonLogsAnalyzer
import org.gradle.integtests.fixtures.daemon.DaemonsFixture
import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
import org.gradle.integtests.fixtures.executer.GradleDistribution
import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
import org.gradle.internal.service.DefaultServiceRegistry
import org.gradle.test.fixtures.file.TestDirectoryProvider
import org.gradle.test.fixtures.file.TestFile
import org.gradle.tooling.GradleConnector
import org.gradle.tooling.ProjectConnection
import org.gradle.tooling.internal.consumer.ConnectorServices
import org.gradle.tooling.internal.consumer.DefaultGradleConnector
import org.gradle.tooling.model.build.BuildEnvironment
import org.gradle.util.GradleVersion
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.concurrent.TimeUnit
class ToolingApi implements TestRule {
private static final Logger LOGGER = LoggerFactory.getLogger(ToolingApi)
private GradleDistribution dist
private TestDirectoryProvider testWorkDirProvider
private TestFile gradleUserHomeDir
private TestFile daemonBaseDir
private boolean useSeparateDaemonBaseDir
private boolean requiresDaemon
private boolean requireIsolatedDaemons
private DefaultServiceRegistry isolatedToolingClient
private context = new IntegrationTestBuildContext()
private final List connectorConfigurers = []
boolean verboseLogging = LOGGER.debugEnabled
ToolingApi(GradleDistribution dist, TestDirectoryProvider testWorkDirProvider) {
this.dist = dist
this.useSeparateDaemonBaseDir = DefaultGradleConnector.metaClass.respondsTo(null, "daemonBaseDir")
this.gradleUserHomeDir = context.gradleUserHomeDir
this.daemonBaseDir = context.daemonBaseDir
this.requiresDaemon = !GradleContextualExecuter.embedded
this.testWorkDirProvider = testWorkDirProvider
}
/**
* Specifies that the test use its own Gradle user home dir and daemon registry.
*/
void requireIsolatedUserHome() {
withUserHome(testWorkDirProvider.testDirectory.file("user-home-dir"))
}
void withUserHome(TestFile userHomeDir) {
gradleUserHomeDir = userHomeDir
useSeparateDaemonBaseDir = false
}
TestFile getDaemonBaseDir() {
return useSeparateDaemonBaseDir ? daemonBaseDir : gradleUserHomeDir.file("daemon")
}
void requireIsolatedToolingApi() {
requireIsolatedDaemons()
isolatedToolingClient = new ConnectorServices.ConnectorServiceRegistry()
}
void close() {
assert isolatedToolingClient != null
isolatedToolingClient.close()
}
/**
* Specifies that the test use real daemon processes (not embedded) and a test-specific daemon registry. Uses a shared Gradle user home dir
*/
void requireIsolatedDaemons() {
if (useSeparateDaemonBaseDir) {
daemonBaseDir = testWorkDirProvider.testDirectory.file("daemons")
} else {
gradleUserHomeDir = testWorkDirProvider.testDirectory.file("user-home-dir")
}
requireIsolatedDaemons = true
requiresDaemon = true
}
/**
* Specifies that the test use real daemon processes (not embedded).
*/
void requireDaemons() {
requiresDaemon = true
}
DaemonsFixture getDaemons() {
return DaemonLogsAnalyzer.newAnalyzer(getDaemonBaseDir(), dist.version.version)
}
void withConnector(Closure cl) {
connectorConfigurers << cl
}
public T withConnection(Closure cl) {
GradleConnector connector = connector()
withConnection(connector, cl)
}
public T withConnection(GradleConnector connector, Closure cl) {
return withConnectionRaw(connector, cl)
}
private validate(Throwable throwable) {
if (dist.version != GradleVersion.current()) {
return
}
// Verify that the exception carries the calling thread's stack information
def currentThreadStack = Thread.currentThread().stackTrace as List
while (!currentThreadStack.empty && (currentThreadStack[0].className != ToolingApi.name || currentThreadStack[0].methodName != 'withConnectionRaw')) {
currentThreadStack.remove(0)
}
assert currentThreadStack.size() > 1
currentThreadStack.remove(0)
String currentThreadStackStr = currentThreadStack.join("\n")
def throwableStack = throwable.stackTrace.join("\n")
assert throwableStack.endsWith(currentThreadStackStr)
}
private T withConnectionRaw(GradleConnector connector, Closure cl) {
ProjectConnection connection = connector.connect()
try {
return connection.with(cl)
} catch (Throwable t) {
validate(t)
throw t
} finally {
connection.close()
}
}
GradleConnector connector() {
DefaultGradleConnector connector
if (isolatedToolingClient != null) {
connector = isolatedToolingClient.getFactory(DefaultGradleConnector).create()
} else {
connector = GradleConnector.newConnector() as DefaultGradleConnector
}
connector.forProjectDirectory(testWorkDirProvider.testDirectory)
if (useClasspathImplementation) {
connector.useClasspathDistribution()
} else {
connector.useInstallation(dist.gradleHomeDir.absoluteFile)
}
connector.embedded(embedded)
connector.searchUpwards(false)
if (useSeparateDaemonBaseDir) {
connector.daemonBaseDir(new File(daemonBaseDir.path))
}
connector.daemonMaxIdleTime(120, TimeUnit.SECONDS)
if (connector.metaClass.hasProperty(connector, 'verboseLogging')) {
connector.verboseLogging = verboseLogging
}
if (gradleUserHomeDir != context.gradleUserHomeDir) {
// When using an isolated user home, first initialise the Gradle instance using the default user home dir
// This sets some some static state that uses files from the use home dir, such as DLLs
connector.useGradleUserHomeDir(new File(context.gradleUserHomeDir.path))
def connection = connector.connect()
try {
connection.getModel(BuildEnvironment.class)
} finally {
connection.close()
}
}
connector.useGradleUserHomeDir(new File(gradleUserHomeDir.path))
connectorConfigurers.each {
connector.with(it)
}
return connector
}
boolean isUseClasspathImplementation() {
// Use classpath implementation only when running tests in embedded mode and for the current Gradle version
return embedded && GradleVersion.current() == dist.version
}
/*
* TODO Stefan the embedded executor has been broken by some
* change after 3.0. It can no longer handle changes to the
* serialized form of tooling models. The current -> 3.0 tests
* are failing as a result. Temporarily deactivating embedded
* mode except for current -> current.
*/
boolean isEmbedded() {
// Use in-process build when running tests in embedded mode and daemon is not required
return GradleContextualExecuter.embedded && !requiresDaemon && GradleVersion.current() == dist.version
}
@Override
Statement apply(Statement base, Description description) {
return new Statement() {
@Override
void evaluate() throws Throwable {
try {
base.evaluate();
} finally {
cleanUpIsolatedDaemonsAndServices()
}
}
}
}
def cleanUpIsolatedDaemonsAndServices() {
if (isolatedToolingClient != null) {
isolatedToolingClient.close()
}
if (requireIsolatedDaemons) {
try {
getDaemons().killAll()
} catch (RuntimeException ex) {
//TODO once we figured out why pid from logfile can be null we should remove this again
LOGGER.warn("Unable to kill daemon(s)", ex)
}
}
if (gradleUserHomeDir != context.gradleUserHomeDir) {
// When the user home directory is not the default for int tests, then the Gradle instance that was used during the test will still be holding some services open in the user home dir (this is by design), so kill off the Gradle instance that was used.
// If we ran in embedded mode, shutdown the embedded services
// If we used the daemons, kill the daemons
// Either way, this is expensive
if (embedded) {
ConnectorServices.reset()
} else {
getDaemons().killAll()
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy