278 lines
12 KiB
Java
278 lines
12 KiB
Java
package qualification;
|
|
|
|
import com.intellij.openapi.application.*;
|
|
import com.intellij.openapi.fileEditor.*;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.startup.StartupActivity;
|
|
import com.intellij.openapi.vfs.*;
|
|
import com.oracle.javafx.scenebuilder.kit.fxom.*;
|
|
import de.neitzel.fxdesigner.kit.*;
|
|
import java.nio.file.*;
|
|
import java.util.concurrent.*;
|
|
import javafx.application.Platform;
|
|
import javafx.embed.swing.JFXPanel;
|
|
import javafx.scene.control.Button;
|
|
|
|
/** Disposable companion plugin, never included in the delivered designer plugin. */
|
|
public final class WorkflowProbe implements StartupActivity.DumbAware {
|
|
static final Path output = Path.of(System.getProperty("designer.check.output"));
|
|
static DesignerPane active;
|
|
static final java.util.concurrent.atomic.AtomicBoolean started =
|
|
new java.util.concurrent.atomic.AtomicBoolean();
|
|
|
|
static <T> T edt(Callable<T> action) throws Exception {
|
|
var f = new CompletableFuture<T>();
|
|
ApplicationManager.getApplication()
|
|
.invokeLater(
|
|
() ->
|
|
WriteIntentReadAction.run(
|
|
() -> {
|
|
try {
|
|
f.complete(action.call());
|
|
} catch (Throwable t) {
|
|
f.completeExceptionally(t);
|
|
}
|
|
}),
|
|
ModalityState.nonModal());
|
|
return f.get(20, TimeUnit.SECONDS);
|
|
}
|
|
|
|
static <T> T fx(Callable<T> action) throws Exception {
|
|
var f = new CompletableFuture<T>();
|
|
Platform.runLater(
|
|
() -> {
|
|
try {
|
|
f.complete(action.call());
|
|
} catch (Throwable t) {
|
|
f.completeExceptionally(t);
|
|
}
|
|
});
|
|
return f.get(20, TimeUnit.SECONDS);
|
|
}
|
|
|
|
static JFXPanel bridge(java.awt.Component c) {
|
|
if (c instanceof JFXPanel p) return p;
|
|
if (c instanceof java.awt.Container parent)
|
|
for (var child : parent.getComponents()) {
|
|
var p = bridge(child);
|
|
if (p != null) return p;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static void await(Callable<Boolean> condition) throws Exception {
|
|
long until = System.nanoTime() + TimeUnit.SECONDS.toNanos(120);
|
|
while (System.nanoTime() < until) {
|
|
if (condition.call()) return;
|
|
Thread.sleep(100);
|
|
}
|
|
throw new AssertionError("IDE workflow timed out");
|
|
}
|
|
|
|
static void click(DesignerPane pane, String text) throws Exception {
|
|
fx(
|
|
() -> {
|
|
((Button)
|
|
pane.lookupAll(".button").stream()
|
|
.filter(n -> n instanceof Button b && b.getText().equals(text))
|
|
.findFirst()
|
|
.orElseThrow())
|
|
.fire();
|
|
return null;
|
|
});
|
|
}
|
|
|
|
public void runActivity(Project project) {
|
|
if (!started.compareAndSet(false, true)) return;
|
|
ApplicationManager.getApplication()
|
|
.executeOnPooledThread(
|
|
() -> {
|
|
try {
|
|
Path path =
|
|
Path.of(
|
|
project.getBasePath(),
|
|
"app/src/main/resources/fixture/components/Person.fxml");
|
|
VirtualFile file =
|
|
edt(() -> LocalFileSystem.getInstance().refreshAndFindFileByNioFile(path));
|
|
FileEditor editor =
|
|
edt(
|
|
() -> {
|
|
var manager = FileEditorManager.getInstance(project);
|
|
var all = manager.openFile(file, true);
|
|
manager.setSelectedEditor(file, "neitzel-fx-designer");
|
|
return java.util.Arrays.stream(all)
|
|
.filter(e -> e.getName().equals("Neitzel Designer"))
|
|
.findFirst()
|
|
.orElseThrow();
|
|
});
|
|
await(() -> edt(() -> bridge(editor.getComponent()) != null));
|
|
JFXPanel bridge = edt(() -> bridge(editor.getComponent()));
|
|
await(
|
|
() ->
|
|
fx(
|
|
() ->
|
|
bridge.getScene() != null
|
|
&& bridge.getScene().getRoot() instanceof DesignerPane));
|
|
DesignerPane pane = fx(() -> (DesignerPane) bridge.getScene().getRoot());
|
|
active = pane;
|
|
await(() -> fx(() -> pane.kit().editor().getFxomDocument() != null));
|
|
click(pane, "Build and Refresh");
|
|
await(
|
|
() ->
|
|
fx(
|
|
() ->
|
|
java.util.List.of("card", "second").stream()
|
|
.allMatch(
|
|
id -> {
|
|
var item =
|
|
new FXOMFxIdIndex(
|
|
pane.kit().editor().getFxomDocument())
|
|
.lookup(id);
|
|
return item instanceof FXOMInstance instance
|
|
&& instance.getSceneGraphObject()
|
|
instanceof SnapshotRegion region
|
|
&& region.image() != null
|
|
&& region.statusText().equals("Target runtime");
|
|
})));
|
|
fx(
|
|
() -> {
|
|
var kit = pane.kit().editor();
|
|
var card =
|
|
(FXOMInstance) new FXOMFxIdIndex(kit.getFxomDocument()).lookup("card");
|
|
kit.getSelection().select(card);
|
|
var property =
|
|
(FXOMPropertyT)
|
|
card.getProperties()
|
|
.get(
|
|
new com.oracle.javafx.scenebuilder.kit.metadata.util
|
|
.PropertyName("caption"));
|
|
String old = property.getValue();
|
|
kit.getJobManager()
|
|
.push(
|
|
new com.oracle.javafx.scenebuilder.kit.editor.job.Job(kit) {
|
|
public boolean isExecutable() {
|
|
return true;
|
|
}
|
|
|
|
public String getDescription() {
|
|
return "IDE caption check";
|
|
}
|
|
|
|
public void execute() {
|
|
set("IDE canvas draft");
|
|
}
|
|
|
|
public void undo() {
|
|
set(old);
|
|
}
|
|
|
|
public void redo() {
|
|
execute();
|
|
}
|
|
|
|
void set(String text) {
|
|
var doc = kit.getFxomDocument();
|
|
doc.beginUpdate();
|
|
try {
|
|
property.setValue(text);
|
|
} finally {
|
|
doc.endUpdate();
|
|
}
|
|
}
|
|
});
|
|
return null;
|
|
});
|
|
await(
|
|
() ->
|
|
edt(
|
|
() ->
|
|
FileDocumentManager.getInstance()
|
|
.getDocument(file)
|
|
.getText()
|
|
.contains("IDE canvas draft")));
|
|
click(pane, "Undo");
|
|
await(
|
|
() ->
|
|
edt(
|
|
() ->
|
|
FileDocumentManager.getInstance()
|
|
.getDocument(file)
|
|
.getText()
|
|
.contains("FXML caption")));
|
|
click(pane, "Redo");
|
|
await(
|
|
() ->
|
|
edt(
|
|
() ->
|
|
FileDocumentManager.getInstance()
|
|
.getDocument(file)
|
|
.getText()
|
|
.contains("IDE canvas draft")));
|
|
click(pane, "Render drafts");
|
|
await(
|
|
() ->
|
|
fx(
|
|
() ->
|
|
java.util.List.of("card", "second").stream()
|
|
.allMatch(
|
|
id -> {
|
|
var item =
|
|
new FXOMFxIdIndex(
|
|
pane.kit().editor().getFxomDocument())
|
|
.lookup(id);
|
|
return item instanceof FXOMInstance instance
|
|
&& instance.getSceneGraphObject()
|
|
instanceof SnapshotRegion region
|
|
&& region.image() != null
|
|
&& region.statusText().equals("Target runtime");
|
|
})));
|
|
fx(
|
|
() -> {
|
|
javax.imageio.ImageIO.write(
|
|
javafx.embed.swing.SwingFXUtils.fromFXImage(
|
|
pane.snapshot(null, null), null),
|
|
"png",
|
|
output.resolve("intellij.png").toFile());
|
|
return null;
|
|
});
|
|
String version = fx(() -> System.getProperty("javafx.runtime.version"));
|
|
if (!version.startsWith("26"))
|
|
throw new AssertionError("Wrong host JavaFX: " + version);
|
|
Files.writeString(
|
|
output.resolve("SUCCESS.txt"),
|
|
"PASS registered IntelliJ editor, shared Kit, real Build and Refresh, target"
|
|
+ " pixels, native document edits, undo/redo, draft render; JavaFX "
|
|
+ version
|
|
+ "\n");
|
|
} catch (Throwable e) {
|
|
try {
|
|
if (active != null)
|
|
fx(
|
|
() -> {
|
|
Files.writeString(
|
|
output.resolve("ui-state.txt"),
|
|
active.lookupAll(".label").stream()
|
|
.filter(n -> n instanceof javafx.scene.control.Label)
|
|
.map(
|
|
n ->
|
|
String.valueOf(
|
|
((javafx.scene.control.Label) n).getText()))
|
|
.toList()
|
|
.toString());
|
|
javax.imageio.ImageIO.write(
|
|
javafx.embed.swing.SwingFXUtils.fromFXImage(
|
|
active.snapshot(null, null), null),
|
|
"png",
|
|
output.resolve("failed.png").toFile());
|
|
return null;
|
|
});
|
|
var w = new java.io.StringWriter();
|
|
e.printStackTrace(new java.io.PrintWriter(w));
|
|
Files.writeString(output.resolve("FAILURE.txt"), w.toString());
|
|
} catch (Exception ignored) {
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|