Connect minimal hosts to snapshots, target workers and shared Kit editing
This commit is contained in:
+193
@@ -0,0 +1,193 @@
|
||||
package de.neitzel.fxdesigner.standalone;
|
||||
|
||||
import de.neitzel.fxdesigner.kit.DesignerPane;
|
||||
import de.neitzel.fxdesigner.workflow.HostLaunch;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/** Development standalone entry: launch settings file and initial FXML path. */
|
||||
public final class DesignerApplication extends Application {
|
||||
private HostLaunch launch;
|
||||
private StandaloneHost host;
|
||||
private int windows;
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
var args = getParameters().getRaw();
|
||||
if (args.size() != 2)
|
||||
throw new IllegalArgumentException("Arguments: local-launch.properties View.fxml");
|
||||
try {
|
||||
launch = HostLaunch.load(Path.of(args.get(0)));
|
||||
open(stage, Path.of(args.get(1)));
|
||||
} catch (Exception setup) {
|
||||
if (host != null) host.close();
|
||||
host =
|
||||
new StandaloneHost(
|
||||
(name, role) -> CompletableFuture.failedFuture(setup),
|
||||
location -> CompletableFuture.failedFuture(setup),
|
||||
diagnostic -> System.err.println(diagnostic.message()));
|
||||
var source =
|
||||
new de.neitzel.fxdesigner.kit.SourceOnlyPane(
|
||||
host.services(), Path.of(args.get(1)), String.valueOf(setup.getMessage()));
|
||||
stage.setTitle("Neitzel FX Designer — source");
|
||||
stage.setScene(new Scene(source, 1000, 700));
|
||||
stage.setOnCloseRequest(
|
||||
event -> {
|
||||
if (!source.dirty()) return;
|
||||
event.consume();
|
||||
var save = new javafx.scene.control.ButtonType("Save");
|
||||
var discard = new javafx.scene.control.ButtonType("Discard");
|
||||
var choice =
|
||||
new javafx.scene.control.Alert(
|
||||
javafx.scene.control.Alert.AlertType.CONFIRMATION,
|
||||
"Unsaved FXML draft",
|
||||
save,
|
||||
discard,
|
||||
javafx.scene.control.ButtonType.CANCEL)
|
||||
.showAndWait()
|
||||
.orElse(javafx.scene.control.ButtonType.CANCEL);
|
||||
if (choice == discard) {
|
||||
stage.setOnCloseRequest(null);
|
||||
stage.close();
|
||||
} else if (choice == save)
|
||||
source
|
||||
.save()
|
||||
.whenComplete(
|
||||
(v, error) ->
|
||||
javafx.application.Platform.runLater(
|
||||
() -> {
|
||||
if (error == null) {
|
||||
stage.setOnCloseRequest(null);
|
||||
stage.close();
|
||||
}
|
||||
}));
|
||||
});
|
||||
stage.setOnHidden(
|
||||
event -> {
|
||||
source.close();
|
||||
host.close();
|
||||
});
|
||||
stage.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void open(Stage stage, Path file) throws Exception {
|
||||
if (host == null)
|
||||
host =
|
||||
new StandaloneHost(
|
||||
launch.runtimes(),
|
||||
location -> {
|
||||
var future = new CompletableFuture<Void>();
|
||||
javafx.application.Platform.runLater(
|
||||
() -> {
|
||||
try {
|
||||
open(new Stage(), Path.of(location.document()));
|
||||
future.complete(null);
|
||||
} catch (Exception e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
},
|
||||
d -> System.err.println(d.message()));
|
||||
var pane = new DesignerPane(host.services(), launch, file);
|
||||
stage.setTitle("Neitzel FX Designer — " + file.getFileName());
|
||||
stage.setScene(new Scene(pane, 1300, 850));
|
||||
windows++;
|
||||
boolean[] closing = {false};
|
||||
stage.setOnCloseRequest(
|
||||
event -> {
|
||||
event.consume();
|
||||
if (closing[0]) return;
|
||||
closing[0] = true;
|
||||
pane.flush()
|
||||
.thenCompose(v -> host.services().documents().unsaved())
|
||||
.whenComplete(
|
||||
(drafts, error) ->
|
||||
javafx.application.Platform.runLater(
|
||||
() -> {
|
||||
if (error != null) {
|
||||
closing[0] = false;
|
||||
new javafx.scene.control.Alert(
|
||||
javafx.scene.control.Alert.AlertType.ERROR,
|
||||
error.getMessage())
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
if (windows > 1 || drafts.isEmpty()) {
|
||||
stage.setOnCloseRequest(null);
|
||||
stage.close();
|
||||
return;
|
||||
}
|
||||
var save = new javafx.scene.control.ButtonType("Save all");
|
||||
var discard = new javafx.scene.control.ButtonType("Discard drafts");
|
||||
var dialog =
|
||||
new javafx.scene.control.Alert(
|
||||
javafx.scene.control.Alert.AlertType.CONFIRMATION,
|
||||
String.join(
|
||||
"\n",
|
||||
drafts.stream().map(d -> d.document().toString()).toList()),
|
||||
save,
|
||||
discard,
|
||||
javafx.scene.control.ButtonType.CANCEL);
|
||||
dialog.setHeaderText("Unsaved FXML drafts");
|
||||
var choice =
|
||||
dialog.showAndWait().orElse(javafx.scene.control.ButtonType.CANCEL);
|
||||
if (choice == javafx.scene.control.ButtonType.CANCEL) {
|
||||
closing[0] = false;
|
||||
return;
|
||||
}
|
||||
if (choice == discard) {
|
||||
stage.setOnCloseRequest(null);
|
||||
stage.close();
|
||||
return;
|
||||
}
|
||||
Thread.ofVirtual()
|
||||
.start(
|
||||
() -> {
|
||||
try {
|
||||
for (var draft : drafts)
|
||||
try (var document =
|
||||
host.services()
|
||||
.documents()
|
||||
.open(draft.document())
|
||||
.toCompletableFuture()
|
||||
.get()) {
|
||||
document
|
||||
.save(draft.revision())
|
||||
.toCompletableFuture()
|
||||
.get();
|
||||
}
|
||||
javafx.application.Platform.runLater(
|
||||
() -> {
|
||||
stage.setOnCloseRequest(null);
|
||||
stage.close();
|
||||
});
|
||||
} catch (Exception failure) {
|
||||
javafx.application.Platform.runLater(
|
||||
() -> {
|
||||
closing[0] = false;
|
||||
new javafx.scene.control.Alert(
|
||||
javafx.scene.control.Alert.AlertType.ERROR,
|
||||
"Save failed: " + failure.getMessage())
|
||||
.show();
|
||||
});
|
||||
}
|
||||
});
|
||||
}));
|
||||
});
|
||||
stage.setOnHidden(
|
||||
e -> {
|
||||
pane.close();
|
||||
if (--windows == 0) host.close();
|
||||
});
|
||||
stage.show();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,14 @@ import java.util.concurrent.*;
|
||||
public final class FileDocuments implements HostServices.Documents, AutoCloseable {
|
||||
private final ExecutorService executor =
|
||||
Executors.newSingleThreadExecutor(Thread.ofVirtual().factory());
|
||||
private final java.util.concurrent.CopyOnWriteArrayList<java.util.function.Consumer<URI>>
|
||||
changes = new java.util.concurrent.CopyOnWriteArrayList<>();
|
||||
|
||||
public AutoCloseable onChange(java.util.function.Consumer<URI> listener) {
|
||||
changes.add(listener);
|
||||
return () -> changes.remove(listener);
|
||||
}
|
||||
|
||||
private final Map<Path, State> documents = new HashMap<>();
|
||||
|
||||
private static final class State {
|
||||
@@ -55,6 +63,10 @@ public final class FileDocuments implements HostServices.Documents, AutoCloseabl
|
||||
});
|
||||
}
|
||||
|
||||
public CompletionStage<List<DocumentSnapshot>> snapshots() {
|
||||
return submit(() -> documents.values().stream().map(FileDocuments::snapshot).toList());
|
||||
}
|
||||
|
||||
public CompletionStage<List<DocumentSnapshot>> unsaved() {
|
||||
return submit(
|
||||
() ->
|
||||
@@ -98,6 +110,7 @@ public final class FileDocuments implements HostServices.Documents, AutoCloseabl
|
||||
s.redo.clear();
|
||||
s.text = updated;
|
||||
s.revision++;
|
||||
changes.forEach(listener -> listener.accept(s.file.toUri()));
|
||||
}
|
||||
return FileDocuments.snapshot(s);
|
||||
});
|
||||
@@ -120,6 +133,7 @@ public final class FileDocuments implements HostServices.Documents, AutoCloseabl
|
||||
to.push(s.text);
|
||||
s.text = from.pop();
|
||||
s.revision++;
|
||||
changes.forEach(listener -> listener.accept(s.file.toUri()));
|
||||
}
|
||||
return FileDocuments.snapshot(s);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.neitzel.fxdesigner.standalone;
|
||||
|
||||
/** Plain launcher supports the development classpath packaging of JavaFX. */
|
||||
public final class Launcher {
|
||||
public static void main(String[] args) {
|
||||
javafx.application.Application.launch(DesignerApplication.class, args);
|
||||
}
|
||||
}
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
package de.neitzel.fxdesigner.standalone;
|
||||
|
||||
import com.oracle.javafx.scenebuilder.kit.fxom.*;
|
||||
import de.neitzel.fxdesigner.kit.*;
|
||||
import de.neitzel.fxdesigner.workflow.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.concurrent.*;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public final class KitWorkflowSmoke {
|
||||
static DesignerPane pane;
|
||||
static Stage stage;
|
||||
static de.neitzel.fxdesigner.kit.SourceOnlyPane fallback;
|
||||
|
||||
static <T> T fx(Callable<T> action) throws Exception {
|
||||
var result = new CompletableFuture<T>();
|
||||
Platform.runLater(
|
||||
() -> {
|
||||
try {
|
||||
result.complete(action.call());
|
||||
} catch (Throwable e) {
|
||||
result.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return result.get(15, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
static void click(String label) throws Exception {
|
||||
fx(
|
||||
() -> {
|
||||
((Button)
|
||||
pane.lookupAll(".button").stream()
|
||||
.filter(n -> n instanceof Button b && b.getText().equals(label))
|
||||
.findFirst()
|
||||
.orElseThrow())
|
||||
.fire();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
static void waitText(de.neitzel.fxdesigner.host.DocumentSession doc, String text)
|
||||
throws Exception {
|
||||
long until = System.nanoTime() + TimeUnit.SECONDS.toNanos(8);
|
||||
while (System.nanoTime() < until) {
|
||||
if (doc.snapshot().toCompletableFuture().get().text().contains(text)) return;
|
||||
Thread.sleep(50);
|
||||
}
|
||||
throw new AssertionError("Document did not reach " + text);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
var started = new CountDownLatch(1);
|
||||
Platform.startup(started::countDown);
|
||||
started.await();
|
||||
Platform.setImplicitExit(false);
|
||||
var launch = HostLaunch.load(Path.of(args[0]));
|
||||
try (var host =
|
||||
new StandaloneHost(
|
||||
launch.runtimes(),
|
||||
l -> CompletableFuture.completedFuture(null),
|
||||
d -> System.out.println(d.message()))) {
|
||||
fx(
|
||||
() -> {
|
||||
pane = new DesignerPane(host.services(), launch, Path.of(args[1]));
|
||||
stage = new Stage();
|
||||
stage.setTitle("Neitzel FX Designer — workflow qualification");
|
||||
stage.setScene(new Scene(pane, 1300, 850));
|
||||
stage.show();
|
||||
return null;
|
||||
});
|
||||
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(20);
|
||||
while (!fx(() -> pane.kit().editor().getFxomDocument() != null)
|
||||
&& System.nanoTime() < deadline) Thread.sleep(100);
|
||||
fx(
|
||||
() -> {
|
||||
if (pane.kit().editor().getFxomDocument() == null)
|
||||
throw new AssertionError("Kit projection unavailable");
|
||||
return null;
|
||||
});
|
||||
click("Build and Refresh");
|
||||
deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(90);
|
||||
boolean rendered = false;
|
||||
while (System.nanoTime() < deadline) {
|
||||
rendered =
|
||||
fx(
|
||||
() ->
|
||||
java.util.List.of("card", "second").stream()
|
||||
.allMatch(
|
||||
id -> {
|
||||
var obj =
|
||||
new FXOMFxIdIndex(pane.kit().editor().getFxomDocument())
|
||||
.lookup(id);
|
||||
return obj instanceof FXOMInstance instance
|
||||
&& instance.getSceneGraphObject() instanceof SnapshotRegion region
|
||||
&& region.image() != null
|
||||
&& region.statusText().equals("Target runtime");
|
||||
}));
|
||||
if (rendered) break;
|
||||
Thread.sleep(100);
|
||||
}
|
||||
if (!rendered) {
|
||||
System.err.println(
|
||||
fx(
|
||||
() ->
|
||||
pane.lookupAll(".label").stream()
|
||||
.filter(n -> n instanceof Label)
|
||||
.map(n -> ((Label) n).getText())
|
||||
.toList()));
|
||||
throw new AssertionError("Shared Kit did not render target pixels");
|
||||
}
|
||||
fx(
|
||||
() -> {
|
||||
var card = new FXOMFxIdIndex(pane.kit().editor().getFxomDocument()).lookup("card");
|
||||
pane.kit().editor().getSelection().select(card);
|
||||
return null;
|
||||
});
|
||||
Thread.sleep(400);
|
||||
fx(
|
||||
() -> {
|
||||
var card =
|
||||
(FXOMInstance)
|
||||
new FXOMFxIdIndex(pane.kit().editor().getFxomDocument()).lookup("card");
|
||||
String caption = ((SnapshotRegion) card.getSceneGraphObject()).getCaption();
|
||||
var field =
|
||||
(TextField)
|
||||
pane.lookupAll(".text-field").stream()
|
||||
.filter(n -> n instanceof TextField t && t.getText().equals(caption))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
field.setText("alpha bravo charlie");
|
||||
pane.applyCss();
|
||||
pane.layout();
|
||||
var result = new StringBuilder();
|
||||
TextSelectionChecks.run(field, result);
|
||||
System.out.print(result);
|
||||
field.setText(caption);
|
||||
field.fireEvent(new javafx.event.ActionEvent());
|
||||
return null;
|
||||
});
|
||||
fx(
|
||||
() -> {
|
||||
var type =
|
||||
com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.I18nStringEditor
|
||||
.class;
|
||||
if (!type.getProtectionDomain()
|
||||
.getCodeSource()
|
||||
.getLocation()
|
||||
.toString()
|
||||
.contains("designer-kit-ui"))
|
||||
throw new AssertionError("Unpatched Kit editor loaded");
|
||||
var snapshot = pane.snapshot(null, null);
|
||||
javax.imageio.ImageIO.write(
|
||||
javafx.embed.swing.SwingFXUtils.fromFXImage(snapshot, null),
|
||||
"png",
|
||||
Path.of(args[2]).toFile());
|
||||
return null;
|
||||
});
|
||||
String originalCaption =
|
||||
fx(
|
||||
() -> {
|
||||
var editor = pane.kit().editor();
|
||||
var card =
|
||||
(FXOMInstance) new FXOMFxIdIndex(editor.getFxomDocument()).lookup("card");
|
||||
var property =
|
||||
(FXOMPropertyT)
|
||||
card.getProperties()
|
||||
.get(
|
||||
new com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName(
|
||||
"caption"));
|
||||
String old = property.getValue();
|
||||
editor
|
||||
.getJobManager()
|
||||
.push(
|
||||
new com.oracle.javafx.scenebuilder.kit.editor.job.Job(editor) {
|
||||
public boolean isExecutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Caption";
|
||||
}
|
||||
|
||||
public void execute() {
|
||||
set("Native canvas edit");
|
||||
}
|
||||
|
||||
public void undo() {
|
||||
set(old);
|
||||
}
|
||||
|
||||
public void redo() {
|
||||
execute();
|
||||
}
|
||||
|
||||
private void set(String value) {
|
||||
var document = editor.getFxomDocument();
|
||||
document.beginUpdate();
|
||||
try {
|
||||
property.setValue(value);
|
||||
} finally {
|
||||
document.endUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
return old;
|
||||
});
|
||||
try (var document =
|
||||
host.services().documents().open(Path.of(args[1]).toUri()).toCompletableFuture().get()) {
|
||||
waitText(document, "Native canvas edit");
|
||||
click("Undo");
|
||||
waitText(document, originalCaption);
|
||||
click("Redo");
|
||||
waitText(document, "Native canvas edit");
|
||||
if (!document.snapshot().toCompletableFuture().get().text().contains("path=\"home\""))
|
||||
throw new AssertionError("Binding metadata lost");
|
||||
click("Save");
|
||||
long until = System.nanoTime() + TimeUnit.SECONDS.toNanos(5);
|
||||
while (!Files.readString(Path.of(args[1])).contains("Native canvas edit")
|
||||
&& System.nanoTime() < until) Thread.sleep(50);
|
||||
if (!Files.readString(Path.of(args[1])).contains("Native canvas edit"))
|
||||
throw new AssertionError("Host save missing");
|
||||
}
|
||||
fx(
|
||||
() -> {
|
||||
pane.close();
|
||||
fallback =
|
||||
new de.neitzel.fxdesigner.kit.SourceOnlyPane(
|
||||
host.services(), Path.of(args[1]), "Unsupported configuration fixture");
|
||||
stage.setScene(new Scene(fallback, 1000, 700));
|
||||
return null;
|
||||
});
|
||||
long readyUntil = System.nanoTime() + TimeUnit.SECONDS.toNanos(5);
|
||||
while (!fx(() -> ((TextArea) fallback.getCenter()).isEditable())
|
||||
&& System.nanoTime() < readyUntil) Thread.sleep(50);
|
||||
fx(
|
||||
() -> {
|
||||
var text = (TextArea) fallback.getCenter();
|
||||
text.appendText("<!-- source remains editable -->");
|
||||
((Button)
|
||||
fallback.lookupAll(".button").stream()
|
||||
.filter(n -> n instanceof Button b && b.getText().equals("Save"))
|
||||
.findFirst()
|
||||
.orElseThrow())
|
||||
.fire();
|
||||
return null;
|
||||
});
|
||||
long savedUntil = System.nanoTime() + TimeUnit.SECONDS.toNanos(5);
|
||||
while (!Files.readString(Path.of(args[1])).contains("source remains editable")
|
||||
&& System.nanoTime() < savedUntil) Thread.sleep(50);
|
||||
if (!Files.readString(Path.of(args[1])).contains("source remains editable"))
|
||||
throw new AssertionError("Source fallback did not save");
|
||||
System.out.println(
|
||||
"PASS native shared Kit: projection, Build and Refresh, worker pixels, selection, patched"
|
||||
+ " class and snapshot");
|
||||
} finally {
|
||||
fx(
|
||||
() -> {
|
||||
if (pane != null) pane.close();
|
||||
if (fallback != null) fallback.close();
|
||||
if (stage != null) stage.close();
|
||||
return null;
|
||||
});
|
||||
Platform.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package de.neitzel.fxdesigner.standalone;
|
||||
|
||||
import javafx.event.Event;
|
||||
import javafx.scene.control.TextInputControl;
|
||||
import javafx.scene.input.*;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
/** Shared real-control mouse event assertions; native device checks are separate. */
|
||||
public final class TextSelectionChecks {
|
||||
static void mouse(
|
||||
TextInputControl t,
|
||||
javafx.event.EventType<MouseEvent> type,
|
||||
int count,
|
||||
int index,
|
||||
boolean down) {
|
||||
Text text = (Text) t.lookup(".text");
|
||||
Text prefix = new Text(t.getText().substring(0, index));
|
||||
prefix.setFont(t.getFont());
|
||||
var point =
|
||||
text.localToScene(
|
||||
prefix.getLayoutBounds().getWidth() + 1, text.getLayoutBounds().getCenterY());
|
||||
var screen = t.getScene().getRoot().localToScreen(point);
|
||||
Event.fireEvent(
|
||||
t,
|
||||
new MouseEvent(
|
||||
type,
|
||||
point.getX(),
|
||||
point.getY(),
|
||||
screen.getX(),
|
||||
screen.getY(),
|
||||
MouseButton.PRIMARY,
|
||||
count,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
down,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
type != MouseEvent.MOUSE_DRAGGED,
|
||||
new PickResult(t, point.getX(), point.getY())));
|
||||
}
|
||||
|
||||
static void click(TextInputControl t, int count, int index) {
|
||||
mouse(t, MouseEvent.MOUSE_PRESSED, count, index, true);
|
||||
mouse(t, MouseEvent.MOUSE_RELEASED, count, index, false);
|
||||
}
|
||||
|
||||
public static void run(TextInputControl t, StringBuilder result) {
|
||||
t.requestFocus();
|
||||
t.positionCaret(t.getLength());
|
||||
click(t, 1, 7);
|
||||
int caret = t.getCaretPosition();
|
||||
boolean single = t.getSelection().getLength() == 0 && caret > 0 && caret < t.getLength();
|
||||
result.append((single ? "PASS" : "FAIL") + " single click: " + t.getSelection() + "\n");
|
||||
t.positionCaret(t.getLength());
|
||||
click(t, 1, 8);
|
||||
click(t, 2, 8);
|
||||
boolean word = "bravo".equals(t.getSelectedText());
|
||||
result.append((word ? "PASS" : "FAIL") + " double click: " + t.getSelectedText() + "\n");
|
||||
t.positionCaret(t.getLength());
|
||||
mouse(t, MouseEvent.MOUSE_PRESSED, 1, 7, true);
|
||||
mouse(t, MouseEvent.MOUSE_DRAGGED, 1, 10, true);
|
||||
mouse(t, MouseEvent.MOUSE_RELEASED, 1, 10, false);
|
||||
boolean drag =
|
||||
t.getSelection().getStart() > 0
|
||||
&& t.getSelection().getEnd() < t.getLength()
|
||||
&& t.getSelection().getLength() > 0;
|
||||
result.append((drag ? "PASS" : "FAIL") + " interior drag: " + t.getSelection() + "\n");
|
||||
t.positionCaret(0);
|
||||
mouse(t, MouseEvent.MOUSE_PRESSED, 1, 10, true);
|
||||
mouse(t, MouseEvent.MOUSE_DRAGGED, 1, 7, true);
|
||||
mouse(t, MouseEvent.MOUSE_RELEASED, 1, 7, false);
|
||||
boolean reverse = t.getAnchor() == 10 && t.getCaretPosition() == 7;
|
||||
result.append(
|
||||
(reverse ? "PASS" : "FAIL")
|
||||
+ " reverse drag: anchor="
|
||||
+ t.getAnchor()
|
||||
+ " caret="
|
||||
+ t.getCaretPosition()
|
||||
+ "\n");
|
||||
click(t, 3, 8);
|
||||
boolean triple = t.getSelectedText().equals(t.getText());
|
||||
result.append((triple ? "PASS" : "FAIL") + " triple click retains select-all\n");
|
||||
if (!single || !word || !drag || !reverse || !triple)
|
||||
throw new AssertionError("Kit must preserve native text selection");
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package de.neitzel.fxdesigner.standalone;
|
||||
|
||||
import de.neitzel.fxdesigner.build.*;
|
||||
import de.neitzel.fxdesigner.workflow.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
/** Separate owner JVM for an actual abrupt-owner-death regression. */
|
||||
public final class WorkerOwnerProbe {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path root = Path.of(args[0]), fixture = Path.of(args[1]), pid = Path.of(args[2]);
|
||||
var store = new SnapshotStore(fixture.resolve(".neitzel-fx-designer/snapshots"));
|
||||
try (var worker =
|
||||
new PreviewWorker(
|
||||
new DraftOverlay(store.lease(store.state("fixture").lastSuccessful()), List.of()))) {
|
||||
worker.start(
|
||||
List.of(
|
||||
root.resolve("designer-target-tools/target/designer-target-tools-0.1.0-SNAPSHOT.jar"),
|
||||
root.resolve("designer-contracts/target/designer-contracts-0.1.0-SNAPSHOT.jar"),
|
||||
root.resolve("designer-preview-api/target/designer-preview-api-0.1.0-SNAPSHOT.jar")),
|
||||
s -> {});
|
||||
Files.writeString(pid, Long.toString(worker.pid()));
|
||||
Thread.sleep(60000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package de.neitzel.fxdesigner.standalone;
|
||||
|
||||
import de.neitzel.fxdesigner.build.*;
|
||||
import de.neitzel.fxdesigner.contracts.*;
|
||||
import de.neitzel.fxdesigner.host.*;
|
||||
import de.neitzel.fxdesigner.workflow.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public final class WorkflowSmoke {
|
||||
static int checks;
|
||||
|
||||
static void check(boolean value, String message) {
|
||||
if (!value) throw new AssertionError(message);
|
||||
checks++;
|
||||
}
|
||||
|
||||
static <T> T await(CompletionStage<T> stage) throws Exception {
|
||||
return stage.toCompletableFuture().get(120, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path root = Path.of(args[0]), java = Path.of(args[1]), fixture = Path.of(args[2]);
|
||||
String mode = args[3];
|
||||
var runtimes =
|
||||
new ConfiguredRuntimes(
|
||||
Map.of(
|
||||
new ConfiguredRuntimes.Key("java21", HostServices.RuntimeRole.BUILD),
|
||||
new HostServices.RuntimeInstallation(java, 21),
|
||||
new ConfiguredRuntimes.Key("java21", HostServices.RuntimeRole.TARGET),
|
||||
new HostServices.RuntimeInstallation(java, 21)));
|
||||
var tools =
|
||||
List.of(
|
||||
root.resolve("designer-target-tools/target/designer-target-tools-0.1.0-SNAPSHOT.jar"),
|
||||
root.resolve("designer-contracts/target/designer-contracts-0.1.0-SNAPSHOT.jar"),
|
||||
root.resolve("designer-preview-api/target/designer-preview-api-0.1.0-SNAPSHOT.jar"));
|
||||
try (var host =
|
||||
new StandaloneHost(
|
||||
runtimes,
|
||||
l -> CompletableFuture.completedFuture(null),
|
||||
d -> System.out.println(d.message()))) {
|
||||
checks = new ConnectedWorkflowContract().run(host.services(), root, fixture);
|
||||
}
|
||||
System.out.println("PASS connected " + mode + " " + checks);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user