Force the user of AstAccessControl to write at least one test violating restriction, implement such tests for existing tests

This is needed because the slightest change in the test setup can make the check useless without the client knowing
The solution is ugly but gives at least some protection against this
This commit is contained in:
Pavel V. Talanov
2014-04-22 17:58:54 +04:00
parent 071553c66b
commit b3898cfb0d
7 changed files with 100 additions and 17 deletions
@@ -1,4 +1,3 @@
// ALLOW_AST_ACCESS
package test package test
public class ClassWithConstVal() : java.lang.Object() { public class ClassWithConstVal() : java.lang.Object() {
@@ -0,0 +1,9 @@
package first
import second.testFun
fun test() {
te<caret>
}
// EXIST: testFun
@@ -18,24 +18,36 @@ package org.jetbrains.jet.completion;
import com.intellij.codeInsight.completion.CompletionTestCase; import com.intellij.codeInsight.completion.CompletionTestCase;
import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElement;
import kotlin.Function0;
import kotlin.Function1; import kotlin.Function1;
import kotlin.Unit;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.completion.util.UtilPackage; import org.jetbrains.jet.completion.util.UtilPackage;
import org.jetbrains.jet.plugin.PluginTestCaseBase; import org.jetbrains.jet.plugin.PluginTestCaseBase;
import org.jetbrains.jet.plugin.project.TargetPlatform; import org.jetbrains.jet.plugin.project.TargetPlatform;
import org.jetbrains.jet.plugin.stubs.AstAccessControl;
public abstract class AbstractMultiFileJvmBasicCompletionTest extends CompletionTestCase { public abstract class AbstractMultiFileJvmBasicCompletionTest extends CompletionTestCase {
protected void doTest(@NotNull String testPath) throws Exception { protected void doTest(@NotNull String testPath) throws Exception {
configureByFile(getTestName(false) + ".kt", ""); configureByFile(getTestName(false) + ".kt", "");
UtilPackage.testCompletion(getFile().getText(), TargetPlatform.JVM, new Function1<Integer, LookupElement[]>() { boolean shouldFail = testPath.contains("NoSpecifiedType");
@Override AstAccessControl.instance$.testWithControlledAccessToAst(
public LookupElement[] invoke(Integer invocationCount) { shouldFail, getFile().getVirtualFile(), getProject(), getTestRootDisposable(),
complete(invocationCount); new Function0<Unit>() {
return myItems; @Override
} public Unit invoke() {
}); UtilPackage.testCompletion(getFile().getText(), TargetPlatform.JVM, new Function1<Integer, LookupElement[]>() {
@Override
public LookupElement[] invoke(Integer invocationCount) {
complete(invocationCount);
return myItems;
}
});
return Unit.VALUE;
}
}
);
} }
@Override @Override
@@ -36,6 +36,11 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/completion/basic/multifile"), Pattern.compile("^([^\\.]+)$"), false); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/completion/basic/multifile"), Pattern.compile("^([^\\.]+)$"), false);
} }
@TestMetadata("CompleteFunctionWithNoSpecifiedType")
public void testCompleteFunctionWithNoSpecifiedType() throws Exception {
doTest("idea/testData/completion/basic/multifile/CompleteFunctionWithNoSpecifiedType");
}
@TestMetadata("CompleteImportedFunction") @TestMetadata("CompleteImportedFunction")
public void testCompleteImportedFunction() throws Exception { public void testCompleteImportedFunction() throws Exception {
doTest("idea/testData/completion/basic/multifile/CompleteImportedFunction"); doTest("idea/testData/completion/basic/multifile/CompleteImportedFunction");
@@ -24,6 +24,8 @@ import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtil;
import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.util.Consumer; import com.intellij.util.Consumer;
import kotlin.Function0;
import kotlin.Unit;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor; import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor; import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor;
@@ -32,7 +34,6 @@ import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.plugin.JetWithJdkAndRuntimeLightProjectDescriptor; import org.jetbrains.jet.plugin.JetWithJdkAndRuntimeLightProjectDescriptor;
import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheService; import org.jetbrains.jet.plugin.caches.resolve.KotlinCacheService;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies; import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
import org.jetbrains.jet.test.util.RecursiveDescriptorComparator; import org.jetbrains.jet.test.util.RecursiveDescriptorComparator;
import org.junit.Assert; import org.junit.Assert;
@@ -52,11 +53,26 @@ public abstract class AbstractLazyResolveByStubTest extends CodeInsightTestCase
doTest(testFileName, false, false); doTest(testFileName, false, false);
} }
public void doTest(@NotNull String path, boolean checkPrimaryConstructors, boolean checkPropertyAccessors) throws Exception { public void doTest(@NotNull final String path, final boolean checkPrimaryConstructors, final boolean checkPropertyAccessors)
throws Exception {
configureByFile(path); configureByFile(path);
AstAccessControl.instance$.prohibitAstAccessForKotlinFiles(getProject(), getTestRootDisposable());
configureModule(getModule(), JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE); configureModule(getModule(), JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE);
ResolveSessionForBodies resolveSession = KotlinCacheService.object$.getInstance(getFile().getProject()).getLazyResolveSession((JetFile) getFile()); boolean shouldFail = getTestName(false).equals("ClassWithConstVal");
AstAccessControl.instance$.testWithControlledAccessToAst(
shouldFail, getProject(), getTestRootDisposable(),
new Function0<Unit>() {
@Override
public Unit invoke() {
performTest(path, checkPrimaryConstructors, checkPropertyAccessors);
return Unit.VALUE;
}
}
);
}
private void performTest(@NotNull String path, boolean checkPrimaryConstructors, boolean checkPropertyAccessors) {
ResolveSessionForBodies resolveSession =
KotlinCacheService.object$.getInstance(getFile().getProject()).getLazyResolveSession((JetFile) getFile());
ModuleDescriptor module = resolveSession.getModuleDescriptor(); ModuleDescriptor module = resolveSession.getModuleDescriptor();
PackageViewDescriptor packageViewDescriptor = module.getPackage(new FqName("test")); PackageViewDescriptor packageViewDescriptor = module.getPackage(new FqName("test"));
Assert.assertNotNull(packageViewDescriptor); Assert.assertNotNull(packageViewDescriptor);
@@ -27,16 +27,39 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VfsUtilCore
import org.jetbrains.jet.InTextDirectivesUtils import org.jetbrains.jet.InTextDirectivesUtils
import junit.framework.TestCase
import kotlin.test.fail
object AstAccessControl { object AstAccessControl {
private val ALLOW_AST_ACCESS_DIRECTIVE = "ALLOW_AST_ACCESS"
val ALLOW_AST_ACCESS_DIRECTIVE = "ALLOW_AST_ACCESS" // Please provide at least one test that fails ast switch check (shouldFail should be true for at least one test)
// This kind of inconvenience is justified by the fact that the check can be invalidated by slight misconfiguration of the test
// leading to all tests passing
fun testWithControlledAccessToAst(shouldFail: Boolean, project: Project, disposable: Disposable, testBody: () -> Unit) {
testWithControlledAccessToAst(shouldFail, listOf(), project, disposable, testBody)
}
fun prohibitAstAccessForKotlinFiles(project: Project, disposable: Disposable) { fun testWithControlledAccessToAst(
shouldFail: Boolean, allowedFile: VirtualFile,
project: Project, disposable: Disposable, testBody: () -> Unit
) {
testWithControlledAccessToAst(shouldFail, listOf(allowedFile), project, disposable, testBody)
}
fun testWithControlledAccessToAst(
shouldFail: Boolean, allowedFiles: List<VirtualFile>,
project: Project, disposable: Disposable, testBody: () -> Unit
) {
setFilter(allowedFiles, disposable, project)
performTest(shouldFail, testBody)
}
private fun setFilter(allowedFiles: List<VirtualFile>, disposable: Disposable, project: Project) {
val manager = (PsiManager.getInstance(project) as PsiManagerImpl) val manager = (PsiManager.getInstance(project) as PsiManagerImpl)
val filter = VirtualFileFilter { val filter = VirtualFileFilter {
file -> file ->
if (file!!.getFileType() != JetFileType.INSTANCE) { if (file!!.getFileType() != JetFileType.INSTANCE || file in allowedFiles) {
false false
} }
else { else {
@@ -47,4 +70,20 @@ object AstAccessControl {
manager.setAssertOnFileLoadingFilter(filter, disposable) manager.setAssertOnFileLoadingFilter(filter, disposable)
} }
} private fun performTest(shouldFail: Boolean, testBody: () -> Unit) {
try {
testBody()
if (shouldFail) {
fail("This failure means that that a test that should fail (by triggering ast switch) in fact did not.\n" +
"This could happen for the following reasons:\n" +
"1. This kind of operation no longer trigger ast switch, choose better indicator test case." +
"2. Test is now misconfigured and no longer checks for ast switch, reconfigure the test.")
}
}
catch (e: Throwable) {
if (!shouldFail) {
throw e
}
}
}
}