Document Designer workflows and qualify the real library example
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
package qualification;
|
||||
|
||||
import com.oracle.javafx.scenebuilder.kit.fxom.*;
|
||||
import de.neitzel.fxdesigner.contracts.*;
|
||||
import de.neitzel.fxdesigner.host.*;
|
||||
import de.neitzel.fxdesigner.kit.*;
|
||||
import de.neitzel.fxdesigner.workflow.*;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.stage.*;
|
||||
|
||||
/** External probe for the actual library example, never shipped in a host or application. */
|
||||
public final class FxExampleProbe {
|
||||
static <T> T fx(Callable<T> action) throws Exception {
|
||||
var answer = new CompletableFuture<T>();
|
||||
Platform.runLater(
|
||||
() -> {
|
||||
try {
|
||||
answer.complete(action.call());
|
||||
} catch (Throwable e) {
|
||||
answer.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return answer.get(20, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
static void await(Callable<Boolean> condition) throws Exception {
|
||||
long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(150);
|
||||
while (System.nanoTime() < end) {
|
||||
if (condition.call()) return;
|
||||
Thread.sleep(100);
|
||||
}
|
||||
throw new AssertionError("fx-example condition timed out");
|
||||
}
|
||||
|
||||
static void check(boolean condition, String message) {
|
||||
if (!condition) throw new AssertionError(message);
|
||||
}
|
||||
|
||||
public static void premain(String arg, Instrumentation ignored) {
|
||||
Thread.ofPlatform()
|
||||
.daemon()
|
||||
.start(
|
||||
() -> {
|
||||
Path output = Path.of(System.getProperty("example.output")),
|
||||
library = Path.of(System.getProperty("example.library"));
|
||||
try {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
try {
|
||||
fx(() -> null);
|
||||
break;
|
||||
} catch (IllegalStateException notStarted) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
await(
|
||||
() ->
|
||||
fx(
|
||||
() ->
|
||||
Window.getWindows().stream()
|
||||
.anyMatch(
|
||||
w ->
|
||||
w instanceof Stage s
|
||||
&& s.getScene() != null
|
||||
&& s.getScene().getRoot()
|
||||
instanceof DesignerPane)));
|
||||
var pane =
|
||||
fx(
|
||||
() ->
|
||||
(DesignerPane)
|
||||
Window.getWindows().stream()
|
||||
.filter(
|
||||
w ->
|
||||
w instanceof Stage s
|
||||
&& s.getScene() != null
|
||||
&& s.getScene().getRoot() instanceof DesignerPane)
|
||||
.map(w -> ((Stage) w).getScene().getRoot())
|
||||
.findFirst()
|
||||
.orElseThrow());
|
||||
await(() -> fx(() -> pane.kit().editor().getFxomDocument() != null));
|
||||
fx(
|
||||
() -> {
|
||||
pane.lookupAll(".button").stream()
|
||||
.filter(
|
||||
n -> n instanceof Button b && b.getText().equals("Build and Refresh"))
|
||||
.map(n -> (Button) n)
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
.fire();
|
||||
return null;
|
||||
});
|
||||
await(
|
||||
() ->
|
||||
fx(
|
||||
() -> {
|
||||
var item =
|
||||
new FXOMFxIdIndex(pane.kit().editor().getFxomDocument())
|
||||
.lookup("homeForm");
|
||||
return item instanceof FXOMInstance i
|
||||
&& i.getSceneGraphObject() instanceof SnapshotRegion r
|
||||
&& r.image() != null
|
||||
&& r.statusText().equals("Target runtime");
|
||||
}));
|
||||
System.out.println(
|
||||
"PASS real fx-example Build and Refresh and canvas Component pixels");
|
||||
var member = DesignerPane.class.getDeclaredField("launch");
|
||||
member.setAccessible(true);
|
||||
var launch = fx(() -> (HostLaunch) member.get(pane));
|
||||
Path desk =
|
||||
library.resolve(
|
||||
"fx-example/src/main/resources/de/neitzel/fx/example/view/Desk.fxml");
|
||||
var host = pane.kit().host();
|
||||
try (var workflow =
|
||||
new DesignerWorkflow(
|
||||
host,
|
||||
launch.configuration(),
|
||||
launch.context(),
|
||||
launch.maven(),
|
||||
launch.tools(),
|
||||
System.out::println);
|
||||
var document =
|
||||
host.documents().open(desk.toUri()).toCompletableFuture().get()) {
|
||||
var original = document.snapshot().toCompletableFuture().get();
|
||||
var metadata =
|
||||
workflow
|
||||
.bindings(false, original, "name", "both")
|
||||
.toCompletableFuture()
|
||||
.get(60, TimeUnit.SECONDS);
|
||||
check(
|
||||
metadata.model().equals("de.neitzel.fx.example.model.Person"),
|
||||
"Wrong declared Model");
|
||||
check(
|
||||
metadata.diagnostics().stream()
|
||||
.anyMatch(s -> s.contains("Generated Model metadata")),
|
||||
"Generated metadata not used");
|
||||
check(
|
||||
metadata.problems().stream().noneMatch(p -> p.severity().equals("ERROR")),
|
||||
"Binding errors: " + metadata.problems());
|
||||
String edited = original.text() + "\n<!-- documented-example edit -->\n";
|
||||
document
|
||||
.apply(
|
||||
new EditBatch(
|
||||
original.revision(),
|
||||
"Reference example edit",
|
||||
List.of(
|
||||
new EditBatch.Replacement(0, original.text().length(), edited))))
|
||||
.toCompletableFuture()
|
||||
.get();
|
||||
check(
|
||||
document.undo().toCompletableFuture().get().text().equals(original.text()),
|
||||
"Undo changed source");
|
||||
check(
|
||||
document.redo().toCompletableFuture().get().text().equals(edited),
|
||||
"Redo changed source");
|
||||
var draft = document.snapshot().toCompletableFuture().get();
|
||||
document.save(draft.revision()).toCompletableFuture().get();
|
||||
check(Files.readString(desk).equals(edited), "Save changed source");
|
||||
var restored = document.undo().toCompletableFuture().get();
|
||||
document.save(restored.revision()).toCompletableFuture().get();
|
||||
var examples =
|
||||
workflow
|
||||
.examplesFor(false, desk.toUri())
|
||||
.toCompletableFuture()
|
||||
.get(60, TimeUnit.SECONDS);
|
||||
check(
|
||||
examples.size() == 1 && examples.getFirst().id().equals("desk"),
|
||||
"Wrong desk samples: " + examples);
|
||||
workflow
|
||||
.interactive(
|
||||
false, "desk", List.of(document.snapshot().toCompletableFuture().get()))
|
||||
.toCompletableFuture()
|
||||
.get(60, TimeUnit.SECONDS);
|
||||
workflow.cancel();
|
||||
int windows = fx(() -> Window.getWindows().size());
|
||||
var child =
|
||||
workflow
|
||||
.componentSource("de.neitzel.fx.example.view.AddressForm")
|
||||
.toCompletableFuture()
|
||||
.get(60, TimeUnit.SECONDS);
|
||||
host.navigation()
|
||||
.open(new HostServices.Location(child, 0))
|
||||
.toCompletableFuture()
|
||||
.get();
|
||||
await(() -> fx(() -> Window.getWindows().size() == windows + 1));
|
||||
host.navigation()
|
||||
.open(new HostServices.Location(child, 0))
|
||||
.toCompletableFuture()
|
||||
.get();
|
||||
check(
|
||||
fx(() -> Window.getWindows().size()) == windows + 1,
|
||||
"Duplicate Component editor");
|
||||
for (var spec :
|
||||
List.of(
|
||||
new String[] {"AddressForm", "address"},
|
||||
new String[] {"VisitForm", "visit"},
|
||||
new String[] {"VisitRow", "row"},
|
||||
new String[] {"NoteForm", "note"})) {
|
||||
var path = desk.resolveSibling(spec[0] + ".fxml");
|
||||
var choices =
|
||||
workflow
|
||||
.examplesFor(false, path.toUri())
|
||||
.toCompletableFuture()
|
||||
.get(60, TimeUnit.SECONDS);
|
||||
check(
|
||||
choices.size() == 1 && choices.getFirst().id().equals(spec[1]),
|
||||
"Wrong component samples: " + choices);
|
||||
try (var component =
|
||||
host.documents().open(path.toUri()).toCompletableFuture().get()) {
|
||||
workflow
|
||||
.interactive(
|
||||
false,
|
||||
spec[1],
|
||||
List.of(
|
||||
document.snapshot().toCompletableFuture().get(),
|
||||
component.snapshot().toCompletableFuture().get()))
|
||||
.toCompletableFuture()
|
||||
.get(60, TimeUnit.SECONDS);
|
||||
workflow.cancel();
|
||||
}
|
||||
}
|
||||
System.out.println(
|
||||
"PASS real fx-example metadata, source history/save, navigation and all five"
|
||||
+ " independent interactive samples");
|
||||
}
|
||||
Files.writeString(
|
||||
output.resolve("workers.txt"),
|
||||
String.join(
|
||||
"\n",
|
||||
ProcessHandle.current()
|
||||
.descendants()
|
||||
.map(p -> Long.toString(p.pid()))
|
||||
.toList()));
|
||||
fx(
|
||||
() -> {
|
||||
for (var window : List.copyOf(Window.getWindows()))
|
||||
if (window instanceof Stage stage) {
|
||||
stage.setOnCloseRequest(null);
|
||||
stage.close();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
Files.writeString(
|
||||
output.resolve("SUCCESS.txt"),
|
||||
"PASS packaged designer with real NeitzelLib fx-example\n");
|
||||
Platform.exit();
|
||||
} catch (Throwable e) {
|
||||
try (var out =
|
||||
new java.io.PrintWriter(
|
||||
Files.newBufferedWriter(output.resolve("FAILURE.txt")))) {
|
||||
e.printStackTrace(out);
|
||||
} catch (Exception ignoredWrite) {
|
||||
}
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user