[Recent Tests] show only failed tests and passed configurations in popup. On right arrow failed test suite and configuration is shown

This commit is contained in:
Yaroslav Lepenkin
2016-07-04 20:47:26 +03:00
parent e4a71067cc
commit 7621f6ae53
8 changed files with 328 additions and 211 deletions
@@ -37,8 +37,8 @@ public interface RecentTestRunner {
}
void setMode(Mode mode);
void run(String url);
void run(RunnerAndConfigurationSettings configuration);
void run(RecentTestsPopupEntry entry);
}
class RecentTestRunnerImpl implements RecentTestRunner {
@@ -67,14 +67,33 @@ class RecentTestRunnerImpl implements RecentTestRunner {
}
@Override
public void run(RunnerAndConfigurationSettings configuration) {
public void run(RecentTestsPopupEntry entry) {
entry.accept(new TestEntryVisitor() {
@Override
public void visitTest(@NotNull SingleTestEntry test) {
run(test.getUrl());
}
@Override
public void visitSuite(@NotNull SuiteEntry suite) {
run(suite.getSuiteUrl());
}
@Override
public void visitRunConfiguration(@NotNull RunConfigurationEntry configuration) {
run(configuration.getRunSettings());
}
});
}
private void run(RunnerAndConfigurationSettings configuration) {
Executor executor = myCurrentAction == RUN ? DefaultRunExecutor.getRunExecutorInstance()
: DefaultDebugExecutor.getDebugExecutorInstance();
ProgramRunnerUtil.executeConfiguration(myProject, configuration, executor);
}
public void run(@NotNull String url) {
private void run(@NotNull String url) {
Location location = myTestLocator.getLocation(url);
if (location == null) {
return;
@@ -25,68 +25,61 @@ fun SuiteEntry.isMyTest(test: SingleTestEntry): Boolean {
return testName.startsWith(this.suiteName)
}
data class SingleTestInfo(val test: SingleTestEntry, val runConfigurationName: String)
class RecentTestsData {
private val runConfigurationSuites = hashMapOf<String, RunConfigurationEntry>()
private var unmatchedRunConfigurationTests = arrayListOf<SingleTestEntry>()
private val urlSuites = mutableListOf<SuiteEntry>()
private var unmatchedUrlTests = mutableListOf<SingleTestEntry>()
private var testsWithoutSuites = arrayListOf<SingleTestInfo>()
fun addSuite(url: String, magnitude: Magnitude, runDate: Date, runConfiguration: RunnerAndConfigurationSettings?) {
val suite = SuiteEntry(url, magnitude, runDate)
if (runConfiguration != null) {
addRunConfigurationSuite(suite, runConfiguration)
}
else {
addUrlSuite(suite)
}
fun addSuite(url: String, runDate: Date, runConfiguration: RunnerAndConfigurationSettings) {
val suite = SuiteEntry(url, runDate)
addRunConfigurationSuite(suite, runConfiguration)
}
fun addTest(url: String, magnitude: Magnitude, runDate: Date, runConfiguration: RunnerAndConfigurationSettings?) {
val test = SingleTestEntry(url, magnitude, runDate)
if (runConfiguration != null) {
addRunConfigurationTest(test, runConfiguration)
}
else {
addUrlTest(test)
}
fun addTest(url: String, magnitude: Magnitude, runDate: Date, runConfiguration: RunnerAndConfigurationSettings) {
val test = SingleTestEntry(url, runDate, magnitude)
addRunConfigurationTest(test, runConfiguration)
}
private fun addUrlSuite(suite: SuiteEntry) {
val suiteTests = unmatchedUrlTests.filter { suite.isMyTest(it) }
suiteTests.forEach { suite.addTest(it) }
unmatchedUrlTests = unmatchedUrlTests.filterTo(arrayListOf(), { !suite.isMyTest(it) })
urlSuites.add(suite)
}
private fun addRunConfigurationSuite(suite: SuiteEntry, config: RunnerAndConfigurationSettings) {
val suiteTests = unmatchedRunConfigurationTests.filter { suite.isMyTest(it) }
suiteTests.forEach { suite.addTest(it) }
moveSuiteTestsToSuite(suite, config)
unmatchedRunConfigurationTests = unmatchedRunConfigurationTests.filterTo(arrayListOf(), { !suite.isMyTest(it) })
val id = config.uniqueID
runConfigurationSuites[id]?.addSuite(suite) ?: runConfigurationSuites.put(id, RunConfigurationEntry(config, suite))
val entry = runConfigurationSuites[id]
if (entry != null) {
entry.addSuite(suite)
}
else {
runConfigurationSuites.put(id, RunConfigurationEntry(config, suite))
}
}
private fun addUrlTest(test: SingleTestEntry) {
findUrlSuite(test.url)?.addTest(test) ?: unmatchedUrlTests.add(test)
private fun moveSuiteTestsToSuite(suite: SuiteEntry, config: RunnerAndConfigurationSettings) {
val filteredTests = arrayListOf<SingleTestInfo>()
testsWithoutSuites.forEach {
if (suite.isMyTest(it.test) && config.name == it.runConfigurationName) {
suite.addTest(it.test)
}
else {
filteredTests.add(it)
}
}
testsWithoutSuites = filteredTests
}
private fun addRunConfigurationTest(test: SingleTestEntry, runConfiguration: RunnerAndConfigurationSettings) {
findRunConfigurationSuite(test.url, runConfiguration)?.addTest(test) ?: unmatchedRunConfigurationTests.add(test)
val suiteEntry = findRunConfigurationSuite(test.url, runConfiguration)
if (suiteEntry != null) {
suiteEntry.addTest(test)
}
else {
testsWithoutSuites.add(SingleTestInfo(test, runConfiguration.name))
}
}
private fun findUrlSuite(url: String) = urlSuites.find {
val testName = VirtualFileManager.extractPath(url)
testName.startsWith(it.suiteName)
}
private fun findRunConfigurationSuite(url: String, runConfiguration: RunnerAndConfigurationSettings): SuiteEntry? {
val pack: RunConfigurationEntry = runConfigurationSuites[runConfiguration.uniqueID] ?: return null
val testName = VirtualFileManager.extractPath(url)
@@ -99,28 +92,69 @@ class RecentTestsData {
return null
}
fun computeConfigurationSuites() = runConfigurationSuites.values
.fold(arrayListOf(), { total: List<SuiteEntry>, entry: RunConfigurationEntry -> total + entry.suites })
fun getTestsToShow(): List<RecentTestsPopupEntry> {
val allConfigurationSuites = computeConfigurationSuites()
unmatchedRunConfigurationTests.forEach {
val currentTest = it
allConfigurationSuites.find { it.isMyTest(currentTest) }?.addTest(currentTest)
val allConfigurations = runConfigurationSuites.values
val allSuites = allConfigurations.fold(arrayListOf<SuiteEntry>(), { list: List<SuiteEntry>, entry -> list + entry.suites })
testsWithoutSuites.forEach {
val info = it
allSuites.find { it.isMyTest(info.test) }?.let { info.test.suite = it }
}
unmatchedUrlTests.forEach {
val currentTest = it
urlSuites.find { it.isMyTest(currentTest) }?.addTest(currentTest)
}
val testsCollector = SingleTestCollector()
allConfigurations.forEach { it.accept(testsCollector) }
val failedTests = testsCollector.tests.filter { it.failed }
return (runConfigurationSuites.values + urlSuites)
.sortedByDescending { it.runDate }
.fold(listOf(), { popupList, currentEntry ->
popupList + currentEntry.getEntriesToShow()
})
val configsCollector = ConfigurationsCollector()
allConfigurations.forEach { it.accept(configsCollector) }
val passedConfigurations = configsCollector.entries.filter { !it.failed }
val entriesToShow = failedTests + passedConfigurations + testsWithoutSuites.map { it.test }.filter { it.suite != null && it.failed }
return entriesToShow.sortedByDescending { it.runDate }
}
}
class UrlsCollector: TestEntryVisitor() {
val urls = mutableListOf<String>()
override fun visitSuite(suite: SuiteEntry) {
urls.add(suite.suiteUrl)
suite.tests.forEach { urls.add(it.url) }
}
override fun visitRunConfiguration(configuration: RunConfigurationEntry) {
configuration.suites.forEach { visitSuite(it) }
}
}
class SingleTestCollector : TestEntryVisitor() {
val tests = mutableListOf<SingleTestEntry>()
override fun visitTest(test: SingleTestEntry) {
tests.add(test)
}
override fun visitSuite(suite: SuiteEntry) {
suite.tests.forEach { it.accept(this) }
}
override fun visitRunConfiguration(configuration: RunConfigurationEntry) {
configuration.suites.forEach { it.accept(this) }
}
}
class ConfigurationsCollector : TestEntryVisitor() {
val entries = mutableListOf<RecentTestsPopupEntry>()
override fun visitRunConfiguration(configuration: RunConfigurationEntry) {
entries.add(configuration)
}
override fun visitSuite(suite: SuiteEntry) {
entries.add(suite)
}
}
@@ -98,8 +98,12 @@ public class RecentTestsListProvider {
}
RunnerAndConfigurationSettings runConfiguration = myConfigurationProvider.getConfiguration(record);
if (runConfiguration == null) {
return;
}
if (TestLocator.isSuite(url)) {
data.addSuite(url, magnitude, record.date, runConfiguration);
data.addSuite(url, record.date, runConfiguration);
}
else {
data.addTest(url, magnitude, record.date, runConfiguration);
@@ -16,72 +16,76 @@
package com.intellij.testIntegration
import com.intellij.execution.RunnerAndConfigurationSettings
import com.intellij.execution.testframework.TestIconMapper
import com.intellij.execution.testframework.sm.runner.states.TestStateInfo
import com.intellij.execution.testframework.sm.runner.states.TestStateInfo.Magnitude.*
import com.intellij.execution.testframework.sm.runner.states.TestStateInfo.Magnitude.ERROR_INDEX
import com.intellij.execution.testframework.sm.runner.states.TestStateInfo.Magnitude.FAILED_INDEX
import com.intellij.icons.AllIcons
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiElement
import java.util.*
import javax.swing.Icon
interface RecentTestsPopupEntry {
val runDate: Date
val magnitude: TestStateInfo.Magnitude
val icon: Icon?
val presentation: String
val testsUrls: List<String>
fun run(runner: RecentTestRunner)
open fun navigatableElement(locator: TestLocator): PsiElement? = null
val runDate: Date
fun getEntriesToShow(): List<RecentTestsPopupEntry>
val failed: Boolean
fun accept(visitor: TestEntryVisitor)
}
open class SingleTestEntry(val url: String,
override val magnitude: TestStateInfo.Magnitude,
override val runDate: Date) : RecentTestsPopupEntry
abstract class TestEntryVisitor {
open fun visitTest(test: SingleTestEntry) = Unit
open fun visitSuite(suite: SuiteEntry) = Unit
open fun visitRunConfiguration(configuration: RunConfigurationEntry) = Unit
}
class SingleTestEntry(val url: String,
override val runDate: Date,
private val magnitude: TestStateInfo.Magnitude) : RecentTestsPopupEntry
{
override val presentation = VirtualFileManager.extractPath(url)
override val testsUrls = listOf(url)
override fun run(runner: RecentTestRunner) {
runner.run(url)
override val icon = TestIconMapper.getIcon(magnitude)
override val failed = magnitude == ERROR_INDEX || magnitude == FAILED_INDEX
var suite: SuiteEntry? = null
override fun accept(visitor: TestEntryVisitor) {
visitor.visitTest(this)
}
override fun navigatableElement(locator: TestLocator) = locator.getLocation(url)?.psiElement
override fun getEntriesToShow(): List<RecentTestsPopupEntry> = listOf(this)
}
class SuiteEntry(url: String, magnitude: TestStateInfo.Magnitude, runDate: Date) : SingleTestEntry(url, magnitude, runDate) {
private val tests = hashSetOf<SingleTestEntry>()
override val testsUrls: List<String>
get() = tests.fold(listOf<String>(), { acc, testEntry -> acc + testEntry.testsUrls })
val suiteName = VirtualFileManager.extractPath(url)
val failedTests: List<SingleTestEntry>
get() = tests.filter { it.magnitude == FAILED_INDEX || it.magnitude == ERROR_INDEX }
fun addTest(info: SingleTestEntry) = tests.add(info)
class SuiteEntry(val suiteUrl: String, override val runDate: Date) : RecentTestsPopupEntry {
override val presentation = suiteName
override fun getEntriesToShow(): List<RecentTestsPopupEntry> {
val failed = failedTests
if (failed.size > 0) {
return failed.sortedByDescending { it.runDate } + this
val tests = hashSetOf<SingleTestEntry>()
val suiteName = VirtualFileManager.extractPath(suiteUrl)
var runConfiguration: RunConfigurationEntry? = null
override val presentation = VirtualFileManager.extractPath(suiteUrl)
override val icon = AllIcons.RunConfigurations.Junit
override val failed: Boolean
get() {
return tests.find { it.failed } != null
}
return listOf(this)
}
override val magnitude: TestStateInfo.Magnitude by lazy {
tests.find { it.magnitude != PASSED_INDEX && it.magnitude != COMPLETE_INDEX }?.magnitude ?: PASSED_INDEX
fun addTest(test: SingleTestEntry) {
tests.add(test)
test.suite = this
}
override fun accept(visitor: TestEntryVisitor) {
visitor.visitSuite(this)
}
}
@@ -93,34 +97,28 @@ class RunConfigurationEntry(val runSettings: RunnerAndConfigurationSettings, ini
addSuite(initial)
}
fun addSuite(s: SuiteEntry) = suites.add(s)
override val runDate: Date
get() {
return suites.minBy { it.runDate }!!.runDate
}
override val failed: Boolean
get() {
return suites.find { it.failed } != null
}
override val runDate = suites.map { it.runDate }.min()!!
override val magnitude: TestStateInfo.Magnitude by lazy {
suites.find { it.magnitude != PASSED_INDEX && it.magnitude != COMPLETE_INDEX }?.magnitude ?: PASSED_INDEX
fun addSuite(suite: SuiteEntry) {
suites.add(suite)
suite.runConfiguration = this
}
override val presentation = runSettings.name
override val testsUrls: List<String>
get() = suites.fold(listOf<String>(), { list, suite -> list + suite.testsUrls })
override fun run(runner: RecentTestRunner) {
runner.run(runSettings)
}
override val icon = AllIcons.RunConfigurations.Junit
override fun getEntriesToShow(): List<RecentTestsPopupEntry> {
if (suites.size == 1) {
return suites[0].getEntriesToShow()
}
return suites
.filter { it.failedTests.size > 0}
.sortedByDescending { it.runDate }
.fold(listOf<RecentTestsPopupEntry>(), { popupList, currentEntry ->
popupList + currentEntry.getEntriesToShow()
}) + this
override fun accept(visitor: TestEntryVisitor) {
visitor.visitRunConfiguration(this)
}
}
}
@@ -15,12 +15,13 @@
*/
package com.intellij.testIntegration
import com.intellij.execution.testframework.TestIconMapper
import com.intellij.icons.AllIcons
import com.intellij.openapi.keymap.MacKeymapUtil
import com.intellij.openapi.ui.popup.ListPopupStep
import com.intellij.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.openapi.util.SystemInfo
import com.intellij.psi.PsiElement
import com.intellij.ui.popup.list.ListPopupImpl
import com.intellij.util.PsiNavigateUtil
import java.awt.event.ActionEvent
@@ -31,9 +32,7 @@ import javax.swing.KeyStroke
class RecentTestsListPopup(popupStep: ListPopupStep<RecentTestsPopupEntry>,
private val testRunner: RecentTestRunner,
private val locator: TestLocator)
: ListPopupImpl(popupStep)
{
private val locator: TestLocator) : ListPopupImpl(popupStep) {
init {
shiftReleased()
@@ -58,16 +57,36 @@ class RecentTestsListPopup(popupStep: ListPopupStep<RecentTestsPopupEntry>,
override fun actionPerformed(e: ActionEvent) {
val values = selectedValues
if (values.size == 1) {
val element = (values[0] as RecentTestsPopupEntry).navigatableElement(locator)
if (element != null) {
val entry = values[0] as RecentTestsPopupEntry
getElement(entry)?.let {
cancel()
PsiNavigateUtil.navigate(element)
PsiNavigateUtil.navigate(it)
}
}
}
})
}
private fun getElement(entry: RecentTestsPopupEntry): PsiElement? {
var element: PsiElement? = null
entry.accept(object : TestEntryVisitor() {
override fun visitTest(test: SingleTestEntry) {
element = locator.getLocation(test.url)?.psiElement
}
override fun visitSuite(suite: SuiteEntry) {
element = locator.getLocation(suite.suiteUrl)?.psiElement
}
override fun visitRunConfiguration(configuration: RunConfigurationEntry) {
if (configuration.suites.size == 1) {
visitSuite(configuration.suites[0])
}
}
})
return element
}
private fun shiftPressed() {
setCaption("Debug Recent Tests")
testRunner.setMode(RecentTestRunner.Mode.DEBUG)
@@ -80,22 +99,99 @@ class RecentTestsListPopup(popupStep: ListPopupStep<RecentTestsPopupEntry>,
}
class SelectTestStep(tests: List<RecentTestsPopupEntry>,
private val runner: RecentTestRunner)
: BaseListPopupStep<RecentTestsPopupEntry>("Debug Recent Tests", tests)
class SelectTestStep(title: String?,
tests: List<RecentTestsPopupEntry>,
private val runner: RecentTestRunner) : BaseListPopupStep<RecentTestsPopupEntry>(title, tests)
{
override fun getIconFor(value: RecentTestsPopupEntry): Icon? {
return TestIconMapper.getIcon(value.magnitude)
if (value is SingleTestEntry) {
return AllIcons.RunConfigurations.TestFailed
}
else {
return AllIcons.RunConfigurations.TestPassed
}
}
override fun getTextFor(value: RecentTestsPopupEntry) = value.presentation
override fun isSpeedSearchEnabled() = true
override fun hasSubstep(selectedValue: RecentTestsPopupEntry) = getConfigurations(selectedValue).isNotEmpty()
override fun onChosen(entry: RecentTestsPopupEntry, finalChoice: Boolean): PopupStep<RecentTestsPopupEntry>? {
entry.run(runner)
return null
if (finalChoice) {
runner.run(entry)
return null
}
val configurations = getConfigurations(entry)
return SelectConfigurationStep(configurations, runner)
}
private fun getConfigurations(entry: RecentTestsPopupEntry): List<RecentTestsPopupEntry> {
val items = mutableListOf<RecentTestsPopupEntry>()
entry.accept(object : TestEntryVisitor() {
override fun visitTest(test: SingleTestEntry) {
val suite = test.suite ?: return
val configuration = suite.runConfiguration
if (configuration == null) {
items.add(suite)
return
}
if (isSingleTestConfiguration(configuration)) {
items.add(suite)
return
}
items.add(configuration)
if (configuration.suites.size > 1) {
items.add(0, suite)
}
}
private fun isSingleTestConfiguration(configuration: RunConfigurationEntry): Boolean {
val suites = configuration.suites
return suites.size == 1 && suites[0].tests.size == 1
}
})
return items
}
}
class SelectConfigurationStep(private val items: List<RecentTestsPopupEntry>,
private val runner: RecentTestRunner)
: BaseListPopupStep<RecentTestsPopupEntry>(null, items)
{
override fun getTextFor(value: RecentTestsPopupEntry): String {
var presentation = value.presentation
value.accept(object : TestEntryVisitor() {
override fun visitSuite(suite: SuiteEntry) {
presentation = "[suite] " + presentation
}
override fun visitRunConfiguration(configuration: RunConfigurationEntry) {
presentation = "[configuration] " + presentation
}
})
return presentation
}
override fun getIconFor(value: RecentTestsPopupEntry?) = AllIcons.RunConfigurations.Junit
override fun onChosen(selectedValue: RecentTestsPopupEntry, finalChoice: Boolean): PopupStep<RecentTestsPopupEntry>? {
if (finalChoice) {
runner.run(selectedValue)
}
return null
}
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.util.Time;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Date;
@@ -60,7 +59,7 @@ public class ShowRecentTests extends AnAction {
List<RecentTestsPopupEntry> entries = listProvider.getTestsToShow();
SelectTestStep selectStepTest = new SelectTestStep(entries, testRunner);
SelectTestStep selectStepTest = new SelectTestStep("Debug Recent Tests", entries, testRunner);
RecentTestsListPopup popup = new RecentTestsListPopup(selectStepTest, testRunner, testLocator);
popup.showCenteredInCurrentWindow(project);
@@ -69,9 +68,9 @@ public class ShowRecentTests extends AnAction {
}
private static void cleanDeadTests(List<RecentTestsPopupEntry> entries, TestLocator testLocator, TestStateStorage testStorage) {
List<String> urls = ContainerUtil.newArrayList();
entries.forEach((entry) -> urls.addAll(entry.getTestsUrls()));
ApplicationManager.getApplication().executeOnPooledThread(new DeadTestsCleaner(testStorage, urls, testLocator));
UrlsCollector collector = new UrlsCollector();
entries.forEach((e) -> e.accept(collector));
ApplicationManager.getApplication().executeOnPooledThread(new DeadTestsCleaner(testStorage, collector.getUrls(), testLocator));
}
}