Files
openide/java/java-tests/testSrc/com/intellij/index/IndexTestGenerator.scala
T

189 lines
7.1 KiB
Scala

/*
* Copyright 2000-2014 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 com.intellij.index
import com.intellij.util.containers.ContainerUtil
import groovy.lang.GroovyClassLoader
import junit.framework.{TestResult, TestCase}
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Gen._
import org.scalacheck.Prop.forAll
import org.scalacheck._
import scala.collection.JavaConversions._
/**
* Run this class to generate randomized tests for IDEA VFS/document/PSI/index subsystem interaction using ScalaCheck.<p/>
*
* When a test fails, a test method code (in Groovy) is printed which should be copied to a normal test class (IndexTest)
* and debugged there.<p/>
*
* The generated test may contain some excessive declarations and checks that should be corrected manually.
* After the fix, that generated test should be renamed according to the underlying issue it found and committed to the repository.
*
* @author peter
*/
object IndexTestGenerator {
val genAction: Gen[Action] = oneOf(
const(Gc),
const(Commit),
const(Save),
for (withImport <- arbitrary[Boolean];
viaDocument <- arbitrary[Boolean])
yield TextChange(viaDocument, withImport),
arbitrary[Boolean] map UpdatePsiClassRef,
arbitrary[Boolean] map UpdatePsiFileRef,
arbitrary[Boolean] map UpdateASTNodeRef,
arbitrary[Boolean] map UpdateDocumentRef
)
val propIndexTest = forAll(Gen.nonEmptyListOf(genAction)) { actions =>
new IndexTestSeq(actions).isSuccessful
}
def main(args: Array[String]) {
propIndexTest.check
}
}
case class IndexTestSeq(actions: List[Action]) {
def printClass: String = {
val sb = StringBuilder.newBuilder
sb.append(prefix)
sb.append(
s"""
|public void "$testName"() {
|def vFile =
| myFixture.addFileToProject("Foo.java", "class Foo {}").virtualFile
|def lastPsiName = "Foo"
|long counterBefore
|Document document
|PsiFile psiFile
|ASTNode astNode
|PsiClass psiClass
|def scope = GlobalSearchScope.allScope(project)
|""".stripMargin)
var changeId = 0
var docClassName = "Foo"
for (action <- actions) {
sb.append("\n")
action match {
case Gc =>
sb.append("PlatformTestUtil.tryGcSoftlyReachableObjects()\n")
case Commit =>
sb.append(
s"""PsiDocumentManager.getInstance(project).commitAllDocuments()
|lastPsiName = "$docClassName"
|""".stripMargin)
case Save =>
sb.append("FileDocumentManager.instance.saveAllDocuments()\n")
case UpdatePsiClassRef(load) =>
sb.append("psiClass = " + (if (load) "JavaPsiFacade.getInstance(project).findClass(lastPsiName, scope)" else "null") + "\n")
if (load) sb.append("assert psiClass\n")
case UpdatePsiFileRef(load) =>
sb.append("psiFile = " + (if (load) "psiManager.findFile(vFile)" else "null") + "\n")
case UpdateASTNodeRef(load) =>
sb.append("astNode = " + (if (load) "JavaPsiFacade.getInstance(project).findClass(lastPsiName, scope).node" else "null") + "\n")
case UpdateDocumentRef(load) =>
sb.append("document = " + (if (load) "FileDocumentManager.instance.getDocument(vFile)" else "null") + "\n")
case TextChange(viaDocument, withImport) =>
changeId += 1
docClassName = "Foo" + changeId
val newText = (if (withImport) "import zoo.Zoo; " else "") + s"class $docClassName {}"
sb.append(
"""counterBefore =
| psiManager.modificationTracker.javaStructureModificationCount
| """.stripMargin)
if (viaDocument) {
sb.append(
s"""FileDocumentManager.instance.getDocument(vFile).text =
| "$newText"
|""".stripMargin)
} else {
sb.append(
s"""//todo remove if statement or replace with its content
|if (FileDocumentManager.instance.unsavedDocuments) {
| FileDocumentManager.instance.saveAllDocuments()
|}
|VfsUtil.saveText(vFile, "$newText")
|""".stripMargin)
}
sb.append(
s"""// todo replace if statement with assertions
|if (!PsiDocumentManager.getInstance(project).uncommittedDocuments) {
| lastPsiName = "$docClassName"
| assert counterBefore !=
| psiManager.modificationTracker.javaStructureModificationCount
|}
|""".stripMargin)
}
}
sb.append("}\n}")
sb.toString()
}
val prefix =
s"""import com.intellij.lang.ASTNode
|import com.intellij.openapi.command.WriteCommandAction
|import com.intellij.openapi.editor.Document
|import com.intellij.openapi.fileEditor.FileDocumentManager
|import com.intellij.openapi.util.Ref
|import com.intellij.openapi.vfs.VfsUtil
|import com.intellij.psi.*
|import com.intellij.psi.search.GlobalSearchScope
|import com.intellij.testFramework.PlatformTestUtil
|import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
|import com.intellij.util.ObjectUtils
|import org.jetbrains.annotations.NotNull
|class DummyTest extends JavaCodeInsightFixtureTestCase {
|protected void invokeTestRunnable(Runnable runnable) {
| WriteCommandAction.runWriteCommandAction(project, runnable)
|}
|""".stripMargin
val testName: String = "test please write a meaningful description here"
def isSuccessful: Boolean = {
println(actions)
val classText = printClass
val test = new GroovyClassLoader().parseClass(classText).newInstance().asInstanceOf[TestCase]
test.setName(testName)
val result: TestResult = test.run()
val successful: Boolean = result.wasSuccessful()
if (!successful) {
ContainerUtil.toList(result.failures()).foreach(failure => println(failure.trace()))
ContainerUtil.toList(result.errors()).foreach(failure => println(failure.trace()))
println(classText)
}
successful
}
}
sealed trait Action
case object Gc extends Action
case object Commit extends Action
case object Save extends Action
case class TextChange(viaDocument: Boolean, withImport: Boolean) extends Action
case class UpdatePsiClassRef(load: Boolean) extends Action
case class UpdatePsiFileRef(load: Boolean) extends Action
case class UpdateDocumentRef(load: Boolean) extends Action
case class UpdateASTNodeRef(load: Boolean) extends Action