/*
* 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.idea.codeInsight;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.PsiModificationTrackerImpl;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade;
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.lazy.ResolveSession;
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
import java.io.File;
import java.io.IOException;
public abstract class AbstractOutOfBlockModificationTest extends KotlinLightCodeInsightFixtureTestCase {
@Override
public void setUp() {
super.setUp();
myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/codeInsight/outOfBlock");
}
@Override
protected String getTestDataPath() {
return PluginTestCaseBase.getTestDataPathBase() + "/codeInsight/outOfBlock";
}
protected void doTest(String path) throws IOException {
myFixture.configureByFile(path);
boolean expectedOutOfBlock = getExpectedOutOfBlockResult();
PsiModificationTrackerImpl tracker =
(PsiModificationTrackerImpl) PsiManager.getInstance(myFixture.getProject()).getModificationTracker();
PsiElement element = myFixture.getFile().findElementAt(myFixture.getCaretOffset());
assertNotNull("Should be valid element", element);
long oobBeforeType = tracker.getOutOfCodeBlockModificationCount();
long modificationCountBeforeType = tracker.getModificationCount();
myFixture.type(getStringToType());
PsiDocumentManager.getInstance(myFixture.getProject()).commitDocument(myFixture.getDocument(myFixture.getFile()));
long oobAfterCount = tracker.getOutOfCodeBlockModificationCount();
long modificationCountAfterType = tracker.getModificationCount();
assertTrue("Modification tracker should always be changed after type", modificationCountBeforeType != modificationCountAfterType);
assertEquals("Result for out of block test is differs from expected on element in file:\n" + FileUtil.loadFile(new File(path)),
expectedOutOfBlock, oobBeforeType != oobAfterCount);
boolean isSkipCheckDefined = InTextDirectivesUtils.isDirectiveDefined(myFixture.getFile().getText(), "SKIP_ANALYZE_CHECK");
assertTrue("It's allowed to skip check with analyze only for tests where out-of-block is expected",
!isSkipCheckDefined || expectedOutOfBlock);
if (!isSkipCheckDefined) {
checkOOBWithDescriptorsResolve(expectedOutOfBlock);
}
}
private void checkOOBWithDescriptorsResolve(boolean expectedOutOfBlock) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
((PsiModificationTrackerImpl) PsiManager.getInstance(myFixture.getProject()).getModificationTracker())
.incOutOfCodeBlockModificationCounter();
}
});
PsiElement updateElement = myFixture.getFile().findElementAt(myFixture.getCaretOffset() - 1);
KtExpression ktExpression = PsiTreeUtil.getParentOfType(updateElement, KtExpression.class, false);
KtDeclaration ktDeclaration = PsiTreeUtil.getParentOfType(updateElement, KtDeclaration.class, false);
KtElement ktElement = ktExpression != null ? ktExpression : ktDeclaration;
if (ktElement == null) return;
ResolutionFacade facade = ResolutionUtils.getResolutionFacade(ktElement.getContainingKtFile());
ResolveSession session = facade.getFrontendService(ResolveSession.class);
session.forceResolveAll();
BindingContext context = session.getBindingContext();
if (ktExpression != null && ktExpression != ktDeclaration) {
@SuppressWarnings("ConstantConditions")
boolean expressionProcessed = context.get(
BindingContext.PROCESSED,
ktExpression instanceof KtFunctionLiteral ? (KtLambdaExpression) ktExpression.getParent() : ktExpression) == Boolean.TRUE;
assertEquals("Expected out-of-block should result expression analyzed and vise versa", expectedOutOfBlock,
expressionProcessed);
}
else {
boolean declarationProcessed = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, ktDeclaration) != null;
assertEquals("Expected out-of-block should result declaration analyzed and vise versa", expectedOutOfBlock,
declarationProcessed);
}
}
private String getStringToType() {
String text = myFixture.getDocument(myFixture.getFile()).getText();
String typeDirectives = InTextDirectivesUtils.findStringWithPrefixes(text, "TYPE:");
return typeDirectives != null ? StringUtil.unescapeStringCharacters(typeDirectives) : "a";
}
private boolean getExpectedOutOfBlockResult() {
String text = myFixture.getDocument(myFixture.getFile()).getText();
boolean expectedOutOfBlock = false;
if (text.startsWith("// TRUE")) {
expectedOutOfBlock = true;
}
else if (text.startsWith("// FALSE")) {
expectedOutOfBlock = false;
}
else {
fail("Expectation of code block result test should be configured with " +
"\"// TRUE\" or \"// FALSE\" directive in the beginning of the file");
}
return expectedOutOfBlock;
}
}