diff --git a/compiler/cli/cli-common/resources/META-INF/extensions/compiler.xml.as42 b/compiler/cli/cli-common/resources/META-INF/extensions/compiler.xml.as42
index 86ede9124a3..9beed2476e6 100644
--- a/compiler/cli/cli-common/resources/META-INF/extensions/compiler.xml.as42
+++ b/compiler/cli/cli-common/resources/META-INF/extensions/compiler.xml.as42
@@ -3,9 +3,6 @@
1.2
-
diff --git a/compiler/cli/cli-common/resources/META-INF/extensions/core.xml.as42 b/compiler/cli/cli-common/resources/META-INF/extensions/core.xml.as42
new file mode 100644
index 00000000000..da66522edb3
--- /dev/null
+++ b/compiler/cli/cli-common/resources/META-INF/extensions/core.xml.as42
@@ -0,0 +1,10 @@
+
+ org.jetbrains.kotlin
+ 1.2
+
+
+
+
+
diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.as42 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.as42
index 552e275cf16..c8683ba6162 100644
--- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.as42
+++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.as42
@@ -504,6 +504,7 @@ class KotlinCoreEnvironment private constructor(
val applicationEnvironment = KotlinCoreApplicationEnvironment.create(parentDisposable, unitTestMode)
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/compiler.xml")
+ registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/core.xml")
registerApplicationServicesForCLI(applicationEnvironment)
registerApplicationServices(applicationEnvironment)
diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/JUnit3RunnerWithInnersForJPS.java.as42 b/compiler/tests-common/tests/org/jetbrains/kotlin/test/JUnit3RunnerWithInnersForJPS.java.as42
new file mode 100644
index 00000000000..dff5a8871a8
--- /dev/null
+++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/JUnit3RunnerWithInnersForJPS.java.as42
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2010-2015 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.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.junit.internal.MethodSorter;
+import org.junit.internal.runners.JUnit38ClassRunner;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.*;
+import org.junit.runner.notification.RunNotifier;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.jetbrains.kotlin.test.JUnit3RunnerWithInners.isTestMethod;
+
+/**
+ * This runner runs class with all inners test classes, but monitors situation when those classes are planned to be executed
+ * with IDEA package test runner.
+ */
+public class JUnit3RunnerWithInnersForJPS extends Runner implements Filterable, Sortable {
+ private static final Set requestedRunners = new HashSet<>();
+
+ private JUnit38ClassRunner delegateRunner;
+ private final Class> klass;
+ private boolean isFakeTest = false;
+
+ public JUnit3RunnerWithInnersForJPS(Class> klass) {
+ this.klass = klass;
+ requestedRunners.add(klass);
+ ensureCompilerExtensionFilesExists();
+ }
+
+ @Override
+ public void run(RunNotifier notifier) {
+ initialize();
+ delegateRunner.run(notifier);
+ }
+
+ @Override
+ public Description getDescription() {
+ initialize();
+ return isFakeTest ? Description.EMPTY : delegateRunner.getDescription();
+ }
+
+ @Override
+ public void filter(Filter filter) throws NoTestsRemainException {
+ initialize();
+ delegateRunner.filter(filter);
+ }
+
+ @Override
+ public void sort(Sorter sorter) {
+ initialize();
+ delegateRunner.sort(sorter);
+ }
+
+ protected void initialize() {
+ if (delegateRunner != null) return;
+ delegateRunner = new JUnit38ClassRunner(getCollectedTests());
+ }
+
+ /**
+ * compiler.xml needs to be in both compiler & ide module for tests execution.
+ * To avoid file duplication copy it to the out dir idea module before test execution.
+ */
+ private static void ensureCompilerExtensionFilesExists() {
+ copyCompilerResourceFile("compiler.xml");
+ copyCompilerResourceFile("core.xml");
+ }
+
+ private static void copyCompilerResourceFile(String fileName) {
+ String compilerXmlSourcePath = "compiler/cli/cli-common/resources/META-INF/extensions/" + fileName;
+
+ String jpsTargetDirectory = "out/production/kotlin.idea.main";
+ String pillTargetDirectory = "out/production/idea.main";
+
+ String baseDir = Files.exists(Paths.get(jpsTargetDirectory)) ? jpsTargetDirectory : pillTargetDirectory;
+ String compilerXmlTargetPath = baseDir + "/META-INF/extensions/" + fileName;
+
+ try {
+ Path targetPath = Paths.get(compilerXmlTargetPath);
+ Files.createDirectories(targetPath.getParent());
+ Files.copy(Paths.get(compilerXmlSourcePath),targetPath, REPLACE_EXISTING);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Test getCollectedTests() {
+ List innerClasses = collectDeclaredClasses(klass, false);
+ Set unprocessedInnerClasses = unprocessedClasses(innerClasses);
+
+ if (unprocessedInnerClasses.isEmpty()) {
+ if (!innerClasses.isEmpty() && !hasTestMethods(klass)) {
+ isFakeTest = true;
+ return new JUnit3RunnerWithInners.FakeEmptyClassTest(klass);
+ }
+ else {
+ return new TestSuite(klass.asSubclass(TestCase.class));
+ }
+ }
+ else if (unprocessedInnerClasses.size() == innerClasses.size()) {
+ return createTreeTestSuite(klass);
+ }
+ else {
+ return new TestSuite(klass.asSubclass(TestCase.class));
+ }
+ }
+
+ private static Test createTreeTestSuite(Class root) {
+ Set classes = new LinkedHashSet<>(collectDeclaredClasses(root, true));
+ Map classSuites = new HashMap<>();
+
+ for (Class aClass : classes) {
+ classSuites.put(aClass, hasTestMethods(aClass) ? new TestSuite(aClass) : new TestSuite(aClass.getCanonicalName()));
+ }
+
+ for (Class aClass : classes) {
+ if (aClass.getEnclosingClass() != null && classes.contains(aClass.getEnclosingClass())) {
+ classSuites.get(aClass.getEnclosingClass()).addTest(classSuites.get(aClass));
+ }
+ }
+
+ return classSuites.get(root);
+ }
+
+ private static Set unprocessedClasses(Collection classes) {
+ Set result = new LinkedHashSet<>();
+ for (Class aClass : classes) {
+ if (!requestedRunners.contains(aClass)) {
+ result.add(aClass);
+ }
+ }
+
+ return result;
+ }
+
+ private static List collectDeclaredClasses(Class klass, boolean withItself) {
+ List result = new ArrayList<>();
+ if (withItself) {
+ result.add(klass);
+ }
+
+ for (Class aClass : klass.getDeclaredClasses()) {
+ result.addAll(collectDeclaredClasses(aClass, true));
+ }
+
+ return result;
+ }
+
+ private static boolean hasTestMethods(Class klass) {
+ for (Class currentClass = klass; Test.class.isAssignableFrom(currentClass); currentClass = currentClass.getSuperclass()) {
+ for (Method each : MethodSorter.getDeclaredMethods(currentClass)) {
+ if (isTestMethod(each)) return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file