diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/AbstractModelBuilderTest.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/AbstractModelBuilderTest.java new file mode 100644 index 00000000000..5516412be50 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/AbstractModelBuilderTest.java @@ -0,0 +1,267 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.jetbrains.kotlin.idea.codeInsight.gradle; + +import com.google.common.collect.Multimap; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.StreamUtil; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling; +import org.gradle.tooling.BuildActionExecuter; +import org.gradle.tooling.GradleConnector; +import org.gradle.tooling.ProjectConnection; +import org.gradle.tooling.internal.consumer.DefaultGradleConnector; +import org.gradle.tooling.model.DomainObjectSet; +import org.gradle.tooling.model.idea.IdeaModule; +import org.gradle.util.GradleVersion; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.BuildScriptClasspathModel; +import org.jetbrains.plugins.gradle.model.ClasspathEntryModel; +import org.jetbrains.plugins.gradle.model.ExternalProject; +import org.jetbrains.plugins.gradle.model.ProjectImportAction; +import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper; +import org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl; +import org.jetbrains.plugins.gradle.util.GradleConstants; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeThat; + +// part of org.jetbrains.plugins.gradle.tooling.builder.AbstractModelBuilderTest +@RunWith(value = Parameterized.class) +public abstract class AbstractModelBuilderTest { + + public static final Object[][] SUPPORTED_GRADLE_VERSIONS = { {"2.9"} }; + + private static final Pattern TEST_METHOD_NAME_PATTERN = Pattern.compile("(.*)\\[(\\d*: with Gradle-.*)\\]"); + + private static File ourTempDir; + + @NotNull + private final String gradleVersion; + private File testDir; + private ProjectImportAction.AllModels allModels; + + @Rule public TestName name = new TestName(); + @Rule public VersionMatcherRule versionMatcherRule = new VersionMatcherRule(); + + public AbstractModelBuilderTest(@NotNull String gradleVersion) { + this.gradleVersion = gradleVersion; + } + + @Parameterized.Parameters(name = "{index}: with Gradle-{0}") + public static Collection data() { + return Arrays.asList(SUPPORTED_GRADLE_VERSIONS); + } + + + @Before + public void setUp() throws Exception { + assumeThat(gradleVersion, versionMatcherRule.getMatcher()); + + ensureTempDirCreated(); + + String methodName = name.getMethodName(); + Matcher m = TEST_METHOD_NAME_PATTERN.matcher(methodName); + if (m.matches()) { + methodName = m.group(1); + } + + testDir = new File(ourTempDir, methodName); + FileUtil.ensureExists(testDir); + + InputStream buildScriptStream = getClass().getResourceAsStream("/" + methodName + "/" + GradleConstants.DEFAULT_SCRIPT_NAME); + try { + FileUtil.writeToFile( + new File(testDir, GradleConstants.DEFAULT_SCRIPT_NAME), + FileUtil.loadTextAndClose(buildScriptStream) + ); + } + finally { + StreamUtil.closeStream(buildScriptStream); + } + + InputStream settingsStream = getClass().getResourceAsStream("/" + methodName + "/" + GradleConstants.SETTINGS_FILE_NAME); + try { + if(settingsStream != null) { + FileUtil.writeToFile( + new File(testDir, GradleConstants.SETTINGS_FILE_NAME), + FileUtil.loadTextAndClose(settingsStream) + ); + } + } finally { + StreamUtil.closeStream(settingsStream); + } + + GradleConnector connector = GradleConnector.newConnector(); + + URI distributionUri = new DistributionLocator().getDistributionFor(GradleVersion.version(gradleVersion)); + connector.useDistribution(distributionUri); + connector.forProjectDirectory(testDir); + int daemonMaxIdleTime = 10; + try { + daemonMaxIdleTime = Integer.parseInt(System.getProperty("gradleDaemonMaxIdleTime", "10")); + } + catch (NumberFormatException ignore) {} + + ((DefaultGradleConnector)connector).daemonMaxIdleTime(daemonMaxIdleTime, TimeUnit.SECONDS); + ProjectConnection connection = connector.connect(); + + try { + ProjectImportAction projectImportAction = new ProjectImportAction(false); + projectImportAction.addExtraProjectModelClasses(getModels()); + BuildActionExecuter buildActionExecutor = connection.action(projectImportAction); + File initScript = GradleExecutionHelper.generateInitScript(false, getToolingExtensionClasses()); + assertNotNull(initScript); + String jdkHome = IdeaTestUtil.requireRealJdkHome(); + buildActionExecutor.setJavaHome(new File(jdkHome)); + buildActionExecutor.setJvmArguments("-Xmx128m", "-XX:MaxPermSize=64m"); + buildActionExecutor.withArguments("--info", "--recompile-scripts", GradleConstants.INIT_SCRIPT_CMD_OPTION, initScript.getAbsolutePath()); + allModels = buildActionExecutor.run(); + assertNotNull(allModels); + } finally { + connection.close(); + } + } + + @NotNull + private static Set getToolingExtensionClasses() { + Set classes = ContainerUtil.set( + ExternalProject.class, + // gradle-tooling-extension-api jar + ProjectImportAction.class, + // gradle-tooling-extension-impl jar + ModelBuildScriptClasspathBuilderImpl.class, + Multimap.class, + ShortTypeHandling.class + ); + + ContainerUtil.addAllNotNull(classes, doGetToolingExtensionClasses()); + return classes; + } + + @NotNull + private static Set doGetToolingExtensionClasses() { + return Collections.emptySet(); + } + + @After + public void tearDown() throws Exception { + if (testDir != null) { + FileUtil.delete(testDir); + } + } + + protected abstract Set getModels(); + + + private Map getModulesMap(final Class aClass) { + DomainObjectSet ideaModules = allModels.getIdeaProject().getModules(); + + final String filterKey = "to_filter"; + Map map = ContainerUtil.map2Map(ideaModules, new Function>() { + @Override + public Pair fun(IdeaModule module) { + T value = allModels.getExtraProject(module, aClass); + String key = value != null ? module.getGradleProject().getPath() : filterKey; + return Pair.create(key, value); + } + }); + + map.remove(filterKey); + return map; + } + + private static void ensureTempDirCreated() throws IOException { + if (ourTempDir != null) return; + + ourTempDir = new File(FileUtil.getTempDirectory(), "gradleTests"); + FileUtil.delete(ourTempDir); + FileUtil.ensureExists(ourTempDir); + } + + public static class DistributionLocator { + private static final String RELEASE_REPOSITORY_ENV = "GRADLE_RELEASE_REPOSITORY"; + private static final String SNAPSHOT_REPOSITORY_ENV = "GRADLE_SNAPSHOT_REPOSITORY"; + private static final String INTELLIJ_LABS_GRADLE_RELEASE_MIRROR = + "http://services.gradle.org-mirror.labs.intellij.net/distributions"; + private static final String INTELLIJ_LABS_GRADLE_SNAPSHOT_MIRROR = + "http://services.gradle.org-mirror.labs.intellij.net/distributions-snapshots"; + private static final String GRADLE_RELEASE_REPO = "http://services.gradle.org/distributions"; + private static final String GRADLE_SNAPSHOT_REPO = "http://services.gradle.org/distributions-snapshots"; + + @NotNull private final String myReleaseRepoUrl; + @NotNull private final String mySnapshotRepoUrl; + + public DistributionLocator() { + this(DistributionLocator.getRepoUrl(false), DistributionLocator.getRepoUrl(true)); + } + + public DistributionLocator(@NotNull String releaseRepoUrl, @NotNull String snapshotRepoUrl) { + myReleaseRepoUrl = releaseRepoUrl; + mySnapshotRepoUrl = snapshotRepoUrl; + } + + @NotNull + public URI getDistributionFor(@NotNull GradleVersion version) throws URISyntaxException { + return getDistribution(getDistributionRepository(version), version, "gradle", "bin"); + } + + @NotNull + private String getDistributionRepository(@NotNull GradleVersion version) { + return version.isSnapshot() ? mySnapshotRepoUrl : myReleaseRepoUrl; + } + + private static URI getDistribution(@NotNull String repositoryUrl, + @NotNull GradleVersion version, + @NotNull String archiveName, + @NotNull String archiveClassifier) throws URISyntaxException { + return new URI(String.format("%s/%s-%s-%s.zip", repositoryUrl, archiveName, version.getVersion(), archiveClassifier)); + } + + @NotNull + public static String getRepoUrl(boolean isSnapshotUrl) { + String envRepoUrl = System.getenv(isSnapshotUrl ? SNAPSHOT_REPOSITORY_ENV : RELEASE_REPOSITORY_ENV); + if (envRepoUrl != null) return envRepoUrl; + + if (UsefulTestCase.IS_UNDER_TEAMCITY) { + return isSnapshotUrl ? INTELLIJ_LABS_GRADLE_SNAPSHOT_MIRROR : INTELLIJ_LABS_GRADLE_RELEASE_MIRROR; + } + + return isSnapshotUrl ? GRADLE_SNAPSHOT_REPO : GRADLE_RELEASE_REPO; + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemImportingTestCase.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemImportingTestCase.java new file mode 100644 index 00000000000..cea72138f3f --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemImportingTestCase.java @@ -0,0 +1,114 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.jetbrains.kotlin.idea.codeInsight.gradle; + +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder; +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode; +import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager; +import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings; +import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.ContainerUtilRt; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.*; + +// part of com.intellij.openapi.externalSystem.test.ExternalSystemImportingTestCase +public abstract class ExternalSystemImportingTestCase extends ExternalSystemTestCase { + @Override + protected Module getModule(String name) { + AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock(); + try { + Module m = ModuleManager.getInstance(myProject).findModuleByName(name); + assertNotNull("Module " + name + " not found", m); + return m; + } + finally { + accessToken.finish(); + } + } + + protected void importProject(@NonNls String config) throws IOException { + createProjectConfig(config); + importProject(); + } + + protected void importProject() { + doImportProject(); + } + + private void doImportProject() { + AbstractExternalSystemSettings systemSettings = ExternalSystemApiUtil.getSettings(myProject, getExternalSystemId()); + ExternalProjectSettings projectSettings = getCurrentExternalProjectSettings(); + projectSettings.setExternalProjectPath(getProjectPath()); + @SuppressWarnings("unchecked") Set projects = ContainerUtilRt.newHashSet(systemSettings.getLinkedProjectsSettings()); + projects.remove(projectSettings); + projects.add(projectSettings); + //noinspection unchecked + systemSettings.setLinkedProjectsSettings(projects); + + final Ref> error = Ref.create(); + ExternalSystemUtil.refreshProjects( + new ImportSpecBuilder(myProject, getExternalSystemId()) + .use(ProgressExecutionMode.MODAL_SYNC) + .callback(new ExternalProjectRefreshCallback() { + @Override + public void onSuccess(@Nullable DataNode externalProject) { + if (externalProject == null) { + System.err.println("Got null External project after import"); + return; + } + ServiceManager.getService(ProjectDataManager.class).importData(externalProject, myProject, true); + System.out.println("External project was successfully imported"); + } + + @Override + public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) { + error.set(Couple.of(errorMessage, errorDetails)); + } + }) + .forceWhenUptodate() + ); + + if (!error.isNull()) { + String failureMsg = "Import failed: " + error.get().first; + if (StringUtil.isNotEmpty(error.get().second)) { + failureMsg += "\nError details: \n" + error.get().second; + } + fail(failureMsg); + } + } + + protected abstract ExternalProjectSettings getCurrentExternalProjectSettings(); + + protected abstract ProjectSystemId getExternalSystemId(); +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemTestCase.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemTestCase.java new file mode 100644 index 00000000000..cc5d74c6f99 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemTestCase.java @@ -0,0 +1,313 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.jetbrains.kotlin.idea.codeInsight.gradle; + +import com.intellij.compiler.CompilerTestUtil; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; +import com.intellij.testFramework.EdtTestUtil; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Processor; +import com.intellij.util.ThrowableRunnable; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; + +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +// part of com.intellij.openapi.externalSystem.test.ExternalSystemTestCase +public abstract class ExternalSystemTestCase extends UsefulTestCase { + private File ourTempDir; + + private IdeaProjectTestFixture myTestFixture; + + protected Project myProject; + + private File myTestDir; + private VirtualFile myProjectRoot; + private final List myAllConfigs = new ArrayList(); + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + ensureTempDirCreated(); + + myTestDir = new File(ourTempDir, getTestName(false)); + FileUtil.ensureExists(myTestDir); + + setUpFixtures(); + myProject = myTestFixture.getProject(); + + edt(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + try { + setUpInWriteAction(); + } + catch (Throwable e) { + try { + tearDown(); + } + catch (Exception e1) { + e1.printStackTrace(); + } + throw new RuntimeException(e); + } + } + }); + } + }); + + List allowedRoots = new ArrayList(); + collectAllowedRoots(allowedRoots); + if (!allowedRoots.isEmpty()) { + VfsRootAccess.allowRootAccess(getTestRootDisposable(), ArrayUtil.toStringArray(allowedRoots)); + } + + CompilerTestUtil.enableExternalCompiler(); + } + + protected void collectAllowedRoots(List roots) throws IOException { + } + + public static Collection collectRootsInside(String root) { + final List roots = ContainerUtil.newSmartList(); + roots.add(root); + FileUtil.processFilesRecursively(new File(root), new Processor() { + @Override + public boolean process(File file) { + try { + String path = file.getCanonicalPath(); + if (!FileUtil.isAncestor(path, path, false)) { + roots.add(path); + } + } + catch (IOException ignore) { + } + return true; + } + }); + + return roots; + } + + private void ensureTempDirCreated() throws IOException { + if (ourTempDir != null) return; + + ourTempDir = new File(FileUtil.getTempDirectory(), getTestsTempDir()); + FileUtil.delete(ourTempDir); + FileUtil.ensureExists(ourTempDir); + } + + protected abstract String getTestsTempDir(); + + private void setUpFixtures() throws Exception { + myTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName()).getFixture(); + myTestFixture.setUp(); + } + + private void setUpInWriteAction() throws Exception { + File projectDir = new File(myTestDir, "project"); + FileUtil.ensureExists(projectDir); + myProjectRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(projectDir); + } + + @After + @Override + public void tearDown() throws Exception { + try { + EdtTestUtil.runInEdtAndWait(new ThrowableRunnable() { + @Override + public void run() throws Throwable { + CompilerTestUtil.disableExternalCompiler(myProject); + tearDownFixtures(); + } + }); + myProject = null; + if (!FileUtil.delete(myTestDir) && myTestDir.exists()) { + System.err.println("Cannot delete " + myTestDir); + //printDirectoryContent(myDir); + myTestDir.deleteOnExit(); + } + } + finally { + super.tearDown(); + resetClassFields(getClass()); + } + } + + private void tearDownFixtures() throws Exception { + myTestFixture.tearDown(); + myTestFixture = null; + } + + private void resetClassFields(Class aClass) { + if (aClass == null) return; + + Field[] fields = aClass.getDeclaredFields(); + for (Field field : fields) { + final int modifiers = field.getModifiers(); + if ((modifiers & Modifier.FINAL) == 0 + && (modifiers & Modifier.STATIC) == 0 + && !field.getType().isPrimitive()) { + field.setAccessible(true); + try { + field.set(this, null); + } + catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + if (aClass == ExternalSystemTestCase.class) return; + resetClassFields(aClass.getSuperclass()); + } + + @Override + protected void runTest() throws Throwable { + try { + super.runTest(); + } + catch (Exception throwable) { + Throwable each = throwable; + do { + if (each instanceof HeadlessException) { + printIgnoredMessage("Doesn't work in Headless environment"); + return; + } + } + while ((each = each.getCause()) != null); + throw throwable; + } + } + + @Override + protected void invokeTestRunnable(@NotNull Runnable runnable) throws Exception { + runnable.run(); + } + + protected static String getRoot() { + if (SystemInfo.isWindows) return "c:"; + return ""; + } + + protected String getProjectPath() { + return myProjectRoot.getPath(); + } + + protected VirtualFile createProjectConfig(@NonNls String config) throws IOException { + return createConfigFile(myProjectRoot, config); + } + + private VirtualFile createConfigFile(final VirtualFile dir, String config) throws IOException { + final String configFileName = getExternalSystemConfigFileName(); + VirtualFile f = dir.findChild(configFileName); + if (f == null) { + f = new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + VirtualFile res = dir.createChildData(null, configFileName); + result.setResult(res); + } + }.execute().getResultObject(); + myAllConfigs.add(f); + } + setFileContent(f, config, true); + return f; + } + + protected abstract String getExternalSystemConfigFileName(); + + protected VirtualFile createProjectSubFile(String relativePath) throws IOException { + File f = new File(getProjectPath(), relativePath); + FileUtil.ensureExists(f.getParentFile()); + FileUtil.ensureCanCreateFile(f); + boolean created = f.createNewFile(); + if(!created) { + throw new AssertionError("Unable to create the project sub file: " + f.getAbsolutePath()); + } + return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f); + } + + protected VirtualFile createProjectSubFile(String relativePath, String content) throws IOException { + VirtualFile file = createProjectSubFile(relativePath); + setFileContent(file, content, false); + return file; + } + + protected Module getModule(String name) { + AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock(); + try { + Module m = ModuleManager.getInstance(myProject).findModuleByName(name); + assertNotNull("Module " + name + " not found", m); + return m; + } + finally { + accessToken.finish(); + } + } + + private static void setFileContent(final VirtualFile file, final String content, final boolean advanceStamps) throws IOException { + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + if (advanceStamps) { + file.setBinaryContent(content.getBytes(CharsetToolkit.UTF8_CHARSET), -1, file.getTimeStamp() + 4000); + } + else { + file.setBinaryContent(content.getBytes(CharsetToolkit.UTF8_CHARSET), file.getModificationStamp(), file.getTimeStamp()); + } + } + }.execute().getResultObject(); + } + + private void printIgnoredMessage(String message) { + String toPrint = "Ignored"; + if (message != null) { + toPrint += ", because " + message; + } + toPrint += ": " + getClass().getSimpleName() + "." + getName(); + System.out.println(toPrint); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleImportingTestCase.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleImportingTestCase.java new file mode 100644 index 00000000000..b78c391aaa3 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleImportingTestCase.java @@ -0,0 +1,226 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.jetbrains.kotlin.idea.codeInsight.gradle; + +import com.intellij.compiler.server.BuildManager; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings; +import com.intellij.openapi.externalSystem.settings.ExternalSystemSettingsListenerAdapter; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.projectRoots.JavaSdk; +import com.intellij.openapi.projectRoots.ProjectJdkTable; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.TestDialog; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.util.PathUtil; +import com.intellij.util.containers.ContainerUtil; +import org.gradle.util.GradleVersion; +import org.gradle.wrapper.GradleWrapperMain; +import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.settings.DistributionType; +import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; +import org.jetbrains.plugins.gradle.settings.GradleSettings; +import org.jetbrains.plugins.gradle.util.GradleConstants; +import org.junit.Rule; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Properties; + +import static org.junit.Assume.assumeThat; + +// part of org.jetbrains.plugins.gradle.importing.GradleImportingTestCase +@RunWith(value = Parameterized.class) +public abstract class GradleImportingTestCase extends ExternalSystemImportingTestCase { + private static final String GRADLE_JDK_NAME = "Gradle JDK"; + private static final int GRADLE_DAEMON_TTL_MS = 10000; + + @Rule public TestName name = new TestName(); + + @Rule public VersionMatcherRule versionMatcherRule = new VersionMatcherRule(); + + @SuppressWarnings({"NullableProblems", "WeakerAccess"}) @NotNull + @Parameterized.Parameter() public String gradleVersion; + + private GradleProjectSettings myProjectSettings; + private String myJdkHome; + + @Override + public void setUp() throws Exception { + myJdkHome = IdeaTestUtil.requireRealJdkHome(); + super.setUp(); + assumeThat(gradleVersion, versionMatcherRule.getMatcher()); + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + Sdk oldJdk = ProjectJdkTable.getInstance().findJdk(GRADLE_JDK_NAME); + if (oldJdk != null) { + ProjectJdkTable.getInstance().removeJdk(oldJdk); + } + VirtualFile jdkHomeDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myJdkHome)); + assert jdkHomeDir != null; + Sdk jdk = SdkConfigurationUtil.setupSdk(new Sdk[0], jdkHomeDir, JavaSdk.getInstance(), true, null, GRADLE_JDK_NAME); + assertNotNull("Cannot create JDK for " + myJdkHome, jdk); + ProjectJdkTable.getInstance().addJdk(jdk); + } + }.execute(); + myProjectSettings = new GradleProjectSettings(); + GradleSettings.getInstance(myProject).setGradleVmOptions("-Xmx128m -XX:MaxPermSize=64m"); + System.setProperty(ExternalSystemExecutionSettings.REMOTE_PROCESS_IDLE_TTL_IN_MS_KEY, String.valueOf(GRADLE_DAEMON_TTL_MS)); + configureWrapper(); + } + + @Override + public void tearDown() throws Exception { + if (myJdkHome == null) { + //super.setUp() wasn't called + return; + } + + try { + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + Sdk old = ProjectJdkTable.getInstance().findJdk(GRADLE_JDK_NAME); + if (old != null) { + SdkConfigurationUtil.removeSdk(old); + } + } + }.execute(); + Messages.setTestDialog(TestDialog.DEFAULT); + FileUtil.delete(BuildManager.getInstance().getBuildSystemDirectory()); + } + finally { + super.tearDown(); + } + } + + @Override + protected void collectAllowedRoots(List roots) throws IOException { + roots.add(myJdkHome); + roots.addAll(collectRootsInside(myJdkHome)); + roots.add(PathManager.getConfigPath()); + } + + @Override + public String getName() { + return name.getMethodName() == null ? super.getName() : FileUtil.sanitizeFileName(name.getMethodName()); + } + + @Parameterized.Parameters(name = "{index}: with Gradle-{0}") + public static Collection data() throws Throwable { + return Arrays.asList(AbstractModelBuilderTest.SUPPORTED_GRADLE_VERSIONS); + } + + @Override + protected String getTestsTempDir() { + return "gradleImportTests"; + } + + @Override + protected String getExternalSystemConfigFileName() { + return "build.gradle"; + } + + @Override + protected void importProject() { + ExternalSystemApiUtil.subscribe(myProject, GradleConstants.SYSTEM_ID, new ExternalSystemSettingsListenerAdapter() { + @Override + public void onProjectsLinked(@NotNull Collection settings) { + Object item = ContainerUtil.getFirstItem(settings); + if (item instanceof GradleProjectSettings) { + ((GradleProjectSettings)item).setGradleJvm(GRADLE_JDK_NAME); + } + } + }); + super.importProject(); + } + + @Override + protected void importProject(@NonNls @Language("Groovy") String config) throws IOException { + config = "allprojects {\n" + + " repositories {\n" + + " maven {\n" + + " url 'http://maven.labs.intellij.net/repo1'\n" + + " }\n" + + " }" + + "}\n" + config; + super.importProject(config); + } + + @Override + protected GradleProjectSettings getCurrentExternalProjectSettings() { + return myProjectSettings; + } + + @Override + protected ProjectSystemId getExternalSystemId() { + return GradleConstants.SYSTEM_ID; + } + + private void configureWrapper() throws IOException, URISyntaxException { + URI distributionUri = new AbstractModelBuilderTest.DistributionLocator().getDistributionFor(GradleVersion.version(gradleVersion)); + + myProjectSettings.setDistributionType(DistributionType.DEFAULT_WRAPPED); + final VirtualFile wrapperJarFrom = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(wrapperJar()); + assert wrapperJarFrom != null; + + final VirtualFile wrapperJarFromTo = createProjectSubFile("gradle/wrapper/gradle-wrapper.jar"); + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + wrapperJarFromTo.setBinaryContent(wrapperJarFrom.contentsToByteArray()); + } + }.execute().throwException(); + + + Properties properties = new Properties(); + properties.setProperty("distributionBase", "GRADLE_USER_HOME"); + properties.setProperty("distributionPath", "wrapper/dists"); + properties.setProperty("zipStoreBase", "GRADLE_USER_HOME"); + properties.setProperty("zipStorePath", "wrapper/dists"); + properties.setProperty("distributionUrl", distributionUri.toString()); + + StringWriter writer = new StringWriter(); + properties.store(writer, null); + + createProjectSubFile("gradle/wrapper/gradle-wrapper.properties", writer.toString()); + } + + @NotNull + private static File wrapperJar() { + return new File(PathUtil.getJarPathForClass(GradleWrapperMain.class)); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleInspectionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleInspectionTest.kt new file mode 100644 index 00000000000..cd326c31b2a --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleInspectionTest.kt @@ -0,0 +1,155 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.jetbrains.kotlin.idea.codeInsight.gradle + +import com.intellij.analysis.AnalysisScope +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.ProblemDescriptorBase +import com.intellij.codeInspection.ex.InspectionManagerEx +import com.intellij.codeInspection.ex.LocalInspectionToolWrapper +import com.intellij.openapi.util.Ref +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.testFramework.InspectionTestUtil +import com.intellij.testFramework.UsefulTestCase +import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl +import org.jetbrains.kotlin.idea.inspections.gradle.DifferentKotlinGradleVersionInspection +import org.jetbrains.kotlin.idea.inspections.gradle.DifferentStdlibGradleVersionInspection +import org.junit.Assert +import org.junit.Test + +class GradleInspectionTest : GradleImportingTestCase() { + @Test + fun testDifferentStdlibGradleVersion() { + val localFile = createProjectSubFile("build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.2") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:1.0.3" + } + """) + importProject() + + val tool = DifferentStdlibGradleVersionInspection() + val problems = getInspectionResult(tool, localFile) + + Assert.assertTrue(problems.size == 1) + Assert.assertEquals("Plugin version (1.0.2) is not the same as library version (1.0.3)", problems.single()) + } + + @Test + fun testDifferentStdlibGradleVersionWithVariables() { + createProjectSubFile("gradle.properties", """ + |kotlin=1.0.1 + |lib_version=1.0.3""".trimMargin()) + val localFile = createProjectSubFile("build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}kotlin") + } + } + + apply plugin: 'kotlin' + + dependencies { + compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: lib_version + } + """) + importProject() + + val tool = DifferentStdlibGradleVersionInspection() + val problems = getInspectionResult(tool, localFile) + + Assert.assertTrue(problems.size == 1) + Assert.assertEquals("Plugin version (1.0.1) is not the same as library version (1.0.3)", problems.single()) + } + + @Test + fun testDifferentKotlinGradleVersion() { + createProjectSubFile("gradle.properties", """test=1.0.1""") + val localFile = createProjectSubFile("build.gradle", """ + group 'Again' + version '1.0-SNAPSHOT' + + buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{test}") + } + } + + apply plugin: 'kotlin' + """) + importProject() + + val tool = DifferentKotlinGradleVersionInspection() + tool.testVersionMessage = "\$PLUGIN_VERSION" + val problems = getInspectionResult(tool, localFile) + + Assert.assertTrue(problems.size == 1) + Assert.assertEquals("Kotlin version that is used for building with Gradle (1.0.1) differs from the one bundled into the IDE plugin (\$PLUGIN_VERSION)", problems.single()) + } + + fun getInspectionResult(tool: LocalInspectionTool, file: VirtualFile): List { + val toolWrapper = LocalInspectionToolWrapper(tool) + + val scope = AnalysisScope(myProject, listOf(file)) + scope.invalidate() + + val inspectionManager = (InspectionManager.getInstance(myProject) as InspectionManagerEx) + val globalContext = CodeInsightTestFixtureImpl.createGlobalContextForTool(scope, myProject, inspectionManager, toolWrapper) + + val resultRef = Ref>() + UsefulTestCase.edt { + InspectionTestUtil.runTool(toolWrapper, scope, globalContext) + val presentation = globalContext.getPresentation(toolWrapper) + + val foundProblems = presentation.problemElements + .values + .flatMap { it.toList() } + .mapNotNull { it as? ProblemDescriptorBase } + .map { it.descriptionTemplate } + + resultRef.set(foundProblems) + } + + return resultRef.get() + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/VersionMatcherRule.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/VersionMatcherRule.java new file mode 100644 index 00000000000..64a42ecb52f --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/VersionMatcherRule.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.jetbrains.kotlin.idea.codeInsight.gradle; + +import org.gradle.util.GradleVersion; +import org.hamcrest.CoreMatchers; +import org.hamcrest.CustomMatcher; +import org.hamcrest.Matcher; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.tooling.annotation.TargetVersions; +import org.jetbrains.plugins.gradle.tooling.util.VersionMatcher; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +// copy of org.jetbrains.plugins.gradle.tooling.VersionMatcherRule +public class VersionMatcherRule extends TestWatcher { + + @Nullable + private CustomMatcher myMatcher; + + @NotNull + public Matcher getMatcher() { + return myMatcher != null ? myMatcher : CoreMatchers.anything(); + } + + @Override + protected void starting(Description d) { + final TargetVersions targetVersions = d.getAnnotation(TargetVersions.class); + if (targetVersions == null) return; + + myMatcher = new CustomMatcher("Gradle version '" + targetVersions.value() + "'") { + @Override + public boolean matches(Object item) { + return item instanceof String && new VersionMatcher(GradleVersion.version(item.toString())).isVersionMatch(targetVersions); + } + }; + } +}