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 fx(Callable action) throws Exception { var answer = new CompletableFuture(); Platform.runLater( () -> { try { answer.complete(action.call()); } catch (Throwable e) { answer.completeExceptionally(e); } }); return answer.get(20, TimeUnit.SECONDS); } static void await(Callable 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\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); } }); } }