mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 22:09:38 +07:00
[performance.tests] AT-579. Completion suggested from the version catalog test. Updated driver. Added allure steps
GitOrigin-RevId: 124bbd238da43264e9359d4ed1ded1cb62958ca1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a094c4bcb7
commit
e49ebf3002
@@ -4,6 +4,7 @@ import com.intellij.driver.client.Driver
|
||||
import com.intellij.driver.client.ProjectRef
|
||||
import com.intellij.driver.client.Remote
|
||||
import com.intellij.driver.client.Timed
|
||||
import com.intellij.driver.client.screenshot.TakeScreenshot
|
||||
import com.intellij.driver.model.LockSemantics
|
||||
import com.intellij.driver.model.OnDispatcher
|
||||
import com.intellij.driver.model.ProductVersion
|
||||
@@ -296,6 +297,10 @@ internal class DriverImpl(host: JmxHost?) : Driver {
|
||||
return try {
|
||||
this.code()
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
new(TakeScreenshot::class).takeScreenshot("beforeKill")
|
||||
throw t
|
||||
}
|
||||
finally {
|
||||
if (currentValue != null) {
|
||||
sessionHolder.set(currentValue)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.intellij.driver.client.screenshot
|
||||
|
||||
import com.intellij.driver.client.Remote
|
||||
|
||||
@Remote("com.jetbrains.performancePlugin.commands.TakeScreenshotCommand", plugin = "com.jetbrains.performancePlugin")
|
||||
interface TakeScreenshot {
|
||||
fun takeScreenshot(childFolder: String?)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.intellij.driver.sdk.commands
|
||||
|
||||
import com.intellij.driver.client.Remote
|
||||
|
||||
@Remote("com.jetbrains.performancePlugin.CommandLogger", plugin = "com.jetbrains.performancePlugin")
|
||||
interface CommandLogger
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.intellij.driver.sdk.commands
|
||||
|
||||
import com.intellij.driver.client.Remote
|
||||
|
||||
@Remote("com.jetbrains.performancePlugin.PlaybackRunnerExtended", plugin = "com.jetbrains.performancePlugin")
|
||||
interface PlaybackRunnerExtended {
|
||||
fun runBlocking(timeoutsMs: Long = 0)
|
||||
}
|
||||
@@ -750,13 +750,23 @@ fun <T : CommandChain> T.showFileHistory(): T {
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T : CommandChain> T.assertCompletionCommand(): T {
|
||||
this.addCommand("${CMD_PREFIX}assertCompletionCommand")
|
||||
fun <T : CommandChain> T.chooseCompletionCommand(completionName: String): T {
|
||||
this.addCommand("${CMD_PREFIX}chooseCompletionCommand ${completionName}")
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T : CommandChain> T.assertCompletionCommand(count: Int): T {
|
||||
this.addCommand("${CMD_PREFIX}assertCompletionCommand ${count}")
|
||||
fun <T : CommandChain> T.assertCompletionCommand(): T {
|
||||
this.addCommand("${CMD_PREFIX}assertCompletionCommand EXIST")
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T : CommandChain> T.assertCompletionCommandContains(completionNames: List<String>): T {
|
||||
this.addCommand("${CMD_PREFIX}assertCompletionCommand CONTAINS ${completionNames.joinToString(" ")}")
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T : CommandChain> T.assertCompletionCommandCount(count: Int): T {
|
||||
this.addCommand("${CMD_PREFIX}assertCompletionCommand COUNT ${count}")
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ public final class BaseCommandProvider implements CommandProvider {
|
||||
Map.entry(CollectAllFilesCommand.PREFIX, CollectAllFilesCommand::new),
|
||||
Map.entry(ExecuteEditorActionCommand.PREFIX, ExecuteEditorActionCommand::new),
|
||||
Map.entry(AssertCompletionCommand.PREFIX, AssertCompletionCommand::new),
|
||||
Map.entry(ChooseCompletionCommand.PREFIX, ChooseCompletionCommand::new),
|
||||
Map.entry(AssertFindUsagesCommand.PREFIX, AssertFindUsagesCommand::new),
|
||||
Map.entry(SetBreakpointCommand.PREFIX, SetBreakpointCommand::new),
|
||||
Map.entry(DebugRunConfigurationCommand.PREFIX, DebugRunConfigurationCommand::new),
|
||||
|
||||
@@ -9,13 +9,25 @@ import org.jetbrains.annotations.NonNls
|
||||
import kotlin.io.path.getLastModifiedTime
|
||||
import kotlin.io.path.listDirectoryEntries
|
||||
|
||||
/**
|
||||
* Command verify last completions. Provides modes: EXIST, COUNT, CONTAINS.
|
||||
* Example: %assertCompletionCommand EXIST - verify that there was at least one completion item
|
||||
* Example: %assertCompletionCommand COUNT {NUM} - verify that there were {NUM} completion items
|
||||
* Example: %assertCompletionCommand CONTAINS {COMPLETION_NAME1} {COMPLETION_NAME2} - verify that completion items contains {COMPLETION_NAME1} {COMPLETION_NAME2}
|
||||
*/
|
||||
class AssertCompletionCommand(text: String, line: Int) : PlaybackCommandCoroutineAdapter(text, line) {
|
||||
companion object {
|
||||
const val PREFIX: @NonNls String = CMD_PREFIX + "assertCompletionCommand"
|
||||
|
||||
private const val EXIST = "EXIST"
|
||||
private const val COUNT = "COUNT"
|
||||
private const val CONTAINS = "CONTAINS"
|
||||
}
|
||||
|
||||
override suspend fun doExecute(context: PlaybackContext) {
|
||||
val completionItemsDir = getCompletionItemsDir()
|
||||
val commandArgs = extractCommandArgument(PREFIX).split(" ").filterNot { it.trim() == "" }
|
||||
val commandArgs = extractCommandArgument(PREFIX).split(" ").filterNot { it.trim() == "" }.toMutableList()
|
||||
val mode = commandArgs.removeAt(0)
|
||||
if (completionItemsDir == null) {
|
||||
throw IllegalStateException("Completion items dump dir not set")
|
||||
}
|
||||
@@ -27,11 +39,21 @@ class AssertCompletionCommand(text: String, line: Int) : PlaybackCommandCoroutin
|
||||
if (data.totalNumber <= 0) {
|
||||
throw IllegalStateException("Expected > 0 completion variants, but got only ${data.totalNumber}")
|
||||
}
|
||||
if (commandArgs.isNotEmpty()) {
|
||||
val expected = commandArgs[0].toInt()
|
||||
if (data.totalNumber != expected) {
|
||||
throw IllegalStateException("Expected ${expected} completion variants, but got ${data.totalNumber}")
|
||||
when (mode) {
|
||||
EXIST -> return
|
||||
COUNT -> {
|
||||
val expected = commandArgs[0].toInt()
|
||||
if (data.totalNumber != expected) {
|
||||
throw IllegalStateException("Expected ${expected} completion variants, but got ${data.totalNumber}")
|
||||
}
|
||||
}
|
||||
CONTAINS -> {
|
||||
val actual = data.items.map { it.name.trim() }
|
||||
if (!actual.containsAll(commandArgs)) {
|
||||
throw IllegalStateException("Actual ${actual} does not contain expected ${commandArgs} completion variants")
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("Specified mode is neither EXIST nor COUNT nor CONTAINS")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.jetbrains.performancePlugin.commands
|
||||
|
||||
import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.LookupManager
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.ui.playback.PlaybackContext
|
||||
import com.intellij.openapi.ui.playback.commands.PlaybackCommandCoroutineAdapter
|
||||
import com.jetbrains.performancePlugin.commands.IdeEditorKeyCommand.pressKey
|
||||
import kotlinx.coroutines.*
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Command chooses a completion item by name. Completion popup should be opened.
|
||||
* Example: %chooseCompletionCommand {COMPLETION_NAME}
|
||||
*/
|
||||
class ChooseCompletionCommand(text: String, line: Int) : PlaybackCommandCoroutineAdapter(text, line) {
|
||||
companion object {
|
||||
const val PREFIX: @NonNls String = CMD_PREFIX + "chooseCompletionCommand"
|
||||
}
|
||||
|
||||
override suspend fun doExecute(context: PlaybackContext) {
|
||||
val completionName = extractCommandArgument(PREFIX).trim()
|
||||
val itemsCount = getLookup(context).items.size
|
||||
for (i in 1..itemsCount) {
|
||||
//we need to get lookup every time because otherwise currentItem is not updated
|
||||
val lookup = getLookup(context)
|
||||
if (lookup.currentItem?.lookupString != completionName) {
|
||||
ApplicationManager.getApplication().invokeAndWait {
|
||||
pressKey(IdeEditorKeyCommand.EditorKey.ARROW_DOWN, context.project)
|
||||
}
|
||||
}
|
||||
else {
|
||||
withContext(Dispatchers.EDT) {
|
||||
ApplicationManager.getApplication().invokeAndWait {
|
||||
lookup.finishLookup(Lookup.NORMAL_SELECT_CHAR)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
throw IllegalArgumentException("There is no completion with name $completionName")
|
||||
}
|
||||
|
||||
private fun getLookup(context: PlaybackContext): LookupImpl {
|
||||
val editor = FileEditorManager.getInstance(context.project).selectedTextEditor!!
|
||||
try {
|
||||
runBlocking {
|
||||
withTimeout(5.seconds) {
|
||||
LookupManager.getActiveLookup(editor) as LookupImpl? == null
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: TimeoutCancellationException) {
|
||||
throw IllegalStateException("There is no lookup after 5 seconds")
|
||||
}
|
||||
return LookupManager.getActiveLookup(editor) as LookupImpl
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,10 @@ import com.intellij.codeInsight.completion.CompletionPhase;
|
||||
import com.intellij.codeInsight.completion.CompletionPhaseListener;
|
||||
import com.intellij.codeInsight.completion.CompletionType;
|
||||
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl;
|
||||
import com.intellij.codeInsight.lookup.*;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupEx;
|
||||
import com.intellij.codeInsight.lookup.LookupListener;
|
||||
import com.intellij.codeInsight.lookup.LookupManager;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -180,14 +183,14 @@ public class CompletionCommand extends PerformanceCommand {
|
||||
}
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private static final class CompletionVariant {
|
||||
public static final class CompletionVariant {
|
||||
@JsonProperty
|
||||
private final String name;
|
||||
|
||||
@JsonCreator
|
||||
private CompletionVariant(@JsonProperty("name") String name) { this.name = name; }
|
||||
|
||||
private String getName() {
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.jetbrains.performancePlugin.commands;
|
||||
|
||||
import com.intellij.platform.diagnostic.telemetry.helpers.TraceUtil;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
@@ -14,19 +13,18 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.playback.PlaybackContext;
|
||||
import com.intellij.openapi.ui.playback.commands.KeyCodeTypeCommand;
|
||||
import com.intellij.openapi.util.ActionCallback;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.platform.diagnostic.telemetry.helpers.TraceUtil;
|
||||
import com.jetbrains.performancePlugin.PerformanceTestSpan;
|
||||
import com.jetbrains.performancePlugin.utils.ActionCallbackProfilerStopper;
|
||||
import com.jetbrains.performancePlugin.utils.EditorUtils;
|
||||
import io.opentelemetry.context.Context;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
import org.jetbrains.concurrency.Promises;
|
||||
|
||||
/**
|
||||
* Command simulates pressing a keyboard key.
|
||||
* Only defined set of key is supported for now: "ENTER", "BACKSPACE", "TAB" and "ESCAPE"
|
||||
* Only defined set of key is supported for now: "ENTER", "BACKSPACE", "TAB", "ESCAPE", "ARROW_DOWN" and "ARROW_UP"
|
||||
* <p>
|
||||
* Syntax: %pressKey <KEY>
|
||||
* Example: %pressKey ENTER
|
||||
@@ -34,7 +32,6 @@ import org.jetbrains.concurrency.Promises;
|
||||
public class IdeEditorKeyCommand extends KeyCodeTypeCommand {
|
||||
|
||||
public static final String PREFIX = CMD_PREFIX + "pressKey";
|
||||
private String actionID;
|
||||
|
||||
public IdeEditorKeyCommand(@NotNull String text, int line) {
|
||||
super(text, line);
|
||||
@@ -45,18 +42,19 @@ public class IdeEditorKeyCommand extends KeyCodeTypeCommand {
|
||||
final ActionCallback actionCallback = new ActionCallbackProfilerStopper();
|
||||
|
||||
String input = extractCommandArgument(PREFIX);
|
||||
switch (StringUtil.toUpperCase(input)) {
|
||||
case "ENTER" -> actionID = IdeActions.ACTION_EDITOR_ENTER;
|
||||
case "BACKSPACE" -> actionID = IdeActions.ACTION_EDITOR_BACKSPACE;
|
||||
case "TAB" -> actionID = IdeActions.ACTION_EDITOR_TAB;
|
||||
case "ESCAPE" -> actionID = IdeActions.ACTION_EDITOR_ESCAPE;
|
||||
default -> {
|
||||
actionCallback.reject("Unknown special character. Please use: ENTER, BACKSPACE, ESCAPE or TAB");
|
||||
return Promises.toPromise(actionCallback);
|
||||
}
|
||||
try {
|
||||
pressKey(EditorKey.valueOf(input), context.getProject());
|
||||
actionCallback.setDone();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
actionCallback.reject(e.getMessage());
|
||||
}
|
||||
|
||||
@Nullable Project project = context.getProject();
|
||||
return Promises.toPromise(actionCallback);
|
||||
}
|
||||
|
||||
public static void pressKey(EditorKey editorKey, Project project) {
|
||||
String actionID = editorKey.action;
|
||||
Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (editor != null) {
|
||||
ApplicationManager.getApplication().runWriteAction(Context.current().wrap(() -> {
|
||||
@@ -70,12 +68,25 @@ public class IdeEditorKeyCommand extends KeyCodeTypeCommand {
|
||||
});
|
||||
span.addEvent("Typing " + actionID);
|
||||
});
|
||||
actionCallback.setDone();
|
||||
}));
|
||||
}
|
||||
else {
|
||||
actionCallback.reject("Editor is not opened");
|
||||
throw new IllegalStateException("Editor is not opened");
|
||||
}
|
||||
}
|
||||
|
||||
public enum EditorKey {
|
||||
ENTER(IdeActions.ACTION_EDITOR_ENTER),
|
||||
BACKSPACE(IdeActions.ACTION_EDITOR_BACKSPACE),
|
||||
TAB(IdeActions.ACTION_EDITOR_TAB),
|
||||
ESCAPE(IdeActions.ACTION_EDITOR_ESCAPE),
|
||||
ARROW_DOWN(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN),
|
||||
ARROW_UP(IdeActions.ACTION_EDITOR_MOVE_CARET_PAGE_UP);
|
||||
|
||||
private final String action;
|
||||
|
||||
EditorKey(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
return Promises.toPromise(actionCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,7 @@ import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.ui.playback.PlaybackContext
|
||||
import com.intellij.openapi.ui.playback.commands.PlaybackCommandCoroutineAdapter
|
||||
import com.intellij.util.ui.ImageUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import kotlinx.coroutines.*
|
||||
import java.awt.*
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.File
|
||||
@@ -34,6 +31,10 @@ private val LOG: Logger
|
||||
* Example: %takeScreenshot onExit
|
||||
</fullPathToFile> */
|
||||
class TakeScreenshotCommand(text: String, line: Int) : PlaybackCommandCoroutineAdapter(text, line) {
|
||||
|
||||
@Suppress("UNUSED") //Needs for Driver
|
||||
constructor() : this("", 0)
|
||||
|
||||
companion object {
|
||||
const val PREFIX: String = CMD_PREFIX + "takeScreenshot"
|
||||
}
|
||||
@@ -41,6 +42,11 @@ class TakeScreenshotCommand(text: String, line: Int) : PlaybackCommandCoroutineA
|
||||
override suspend fun doExecute(context: PlaybackContext) {
|
||||
takeScreenshotOfAllWindows(extractCommandArgument(PREFIX).ifEmpty { "beforeExit" })
|
||||
}
|
||||
|
||||
@Suppress("UNUSED") //Needs for Driver
|
||||
fun takeScreenshot(childFolder: String?) {
|
||||
runBlocking { takeScreenshotOfAllWindows(childFolder) }
|
||||
}
|
||||
}
|
||||
|
||||
fun takeScreenshotWithAwtRobot(fullPathToFile: String) {
|
||||
@@ -69,7 +75,7 @@ fun takeScreenshotWithAwtRobot(fullPathToFile: String) {
|
||||
}
|
||||
|
||||
suspend fun captureComponent(component: Component, file: File) {
|
||||
if(component.width == 0 || component.height == 0) {
|
||||
if (component.width == 0 || component.height == 0) {
|
||||
LOG.info(component.name + " has zero size, skipping")
|
||||
LOG.info(component.javaClass.toString())
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user