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
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.lookup.LookupElement;
import kotlin.Function0;
import kotlin.Function1;
import kotlin.Unit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.completion.util.UtilPackage;
import org.jetbrains.jet.plugin.PluginTestCaseBase;
import org.jetbrains.jet.plugin.project.TargetPlatform;
import org.jetbrains.jet.plugin.stubs.AstAccessControl;
public abstract class AbstractMultiFileJvmBasicCompletionTest extends CompletionTestCase {
protected void doTest(@NotNull String testPath) throws Exception {
configureByFile(getTestName(false) + ".kt", "");
UtilPackage.testCompletion(getFile().getText(), TargetPlatform.JVM, new Function1<Integer, LookupElement[]>() {
@Override
public LookupElement[] invoke(Integer invocationCount) {
complete(invocationCount);
return myItems;
}
});
boolean shouldFail = testPath.contains("NoSpecifiedType");
AstAccessControl.instance$.testWithControlledAccessToAst(
shouldFail, getFile().getVirtualFile(), getProject(), getTestRootDisposable(),
new Function0<Unit>() {
@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
@@ -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);
}
@TestMetadata("CompleteFunctionWithNoSpecifiedType")
public void testCompleteFunctionWithNoSpecifiedType() throws Exception {
doTest("idea/testData/completion/basic/multifile/CompleteFunctionWithNoSpecifiedType");
}
@TestMetadata("CompleteImportedFunction")
public void testCompleteImportedFunction() throws Exception {
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.testFramework.LightProjectDescriptor;
import com.intellij.util.Consumer;
import kotlin.Function0;
import kotlin.Unit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
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.plugin.JetWithJdkAndRuntimeLightProjectDescriptor;
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.test.util.RecursiveDescriptorComparator;
import org.junit.Assert;
@@ -52,11 +53,26 @@ public abstract class AbstractLazyResolveByStubTest extends CodeInsightTestCase
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);
AstAccessControl.instance$.prohibitAstAccessForKotlinFiles(getProject(), getTestRootDisposable());
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();
PackageViewDescriptor packageViewDescriptor = module.getPackage(new FqName("test"));
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.VfsUtilCore
import org.jetbrains.jet.InTextDirectivesUtils
import junit.framework.TestCase
import kotlin.test.fail
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 filter = VirtualFileFilter {
file ->
if (file!!.getFileType() != JetFileType.INSTANCE) {
if (file!!.getFileType() != JetFileType.INSTANCE || file in allowedFiles) {
false
}
else {
@@ -47,4 +70,20 @@ object AstAccessControl {
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
}
}
}
}