✅ Fix tests that checks for a window’s component hierarchy
authorDavid Roden <github-a8in@qsheltier.de>
Mon, 27 Jan 2025 20:44:33 +0000 (21:44 +0100)
committerDavid Roden <github-a8in@qsheltier.de>
Wed, 29 Jan 2025 20:06:50 +0000 (21:06 +0100)
server/src/main/java/de/qsheltier/msta/Server.java
server/src/test/java/de/qsheltier/msta/ServerTest.java

index 4ca790b..cb12dbb 100644 (file)
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import java.awt.Component;
+import java.awt.Container;
 import java.awt.Frame;
 import java.awt.Window;
 import java.io.BufferedReader;
@@ -24,10 +25,14 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
+import javax.swing.JButton;
+import javax.swing.JLabel;
 import javax.swing.SwingUtilities;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -150,7 +155,7 @@ public class Server implements Closeable {
                        var newWindows = currentlyOpenWindows.stream().filter(window -> !windows.containsValue(window)).toList();
                        newWindows.forEach(newWindow -> {
                                synchronized (outputWriters) {
-                                       outputWriters.forEach(writeLine -> writeLine.accept(createEvent("window-opened", getWindowObject(newWindow))));
+                                       outputWriters.forEach(writeLine -> writeLine.accept(createEvent("window-opened", getComponentObject(newWindow))));
                                }
                                windows.put(newWindow.getName(), newWindow);
                        });
@@ -187,7 +192,7 @@ public class Server implements Closeable {
                                        if (words.getFirst().equalsIgnoreCase("info") && (words.size() == 3) && words.get(1).equalsIgnoreCase("window")) {
                                                var windowName = words.get(2);
                                                stream(Window.getWindows()).filter(window -> window.getName().equals(windowName))
-                                                               .forEach(window -> writeLine.accept(createMessage(getWindowObject(window).put("info", "window"))));
+                                                               .forEach(window -> writeLine.accept(createMessage(getComponentObject(window).put("info", "window"))));
                                        }
                                }
                        } finally {
@@ -198,11 +203,27 @@ public class Server implements Closeable {
                }
        }
 
-       private ObjectNode getWindowObject(Window window) {
-               var windowNode = objectMapper.createObjectNode();
-               windowNode.put("id", window.getName());
-               windowNode.put("is-frame", window instanceof Frame);
-               return windowNode;
+       private ObjectNode getComponentObject(Component component) {
+               var componentNode = objectMapper.createObjectNode();
+               componentNode.put("id", componentIds.computeIfAbsent(component, c -> idCounter.incrementAndGet()));
+               componentNode.put("type", component.getClass().getName());
+               switch (component) {
+                       case Frame frame -> componentNode.put("title", frame.getTitle());
+                       case JButton button -> componentNode.put("text", button.getText());
+                       case JLabel label -> componentNode.put("text", label.getText());
+                       case Container container -> {
+                               var childrenNode = componentNode.putArray("children");
+                               synchronized (component.getTreeLock()) {
+                                       stream(container.getComponents())
+                                                       .filter(Component::isVisible)
+                                                       .map(this::getComponentObject)
+                                                       .forEach(childrenNode::add);
+                               }
+                       }
+                       default -> {
+                       }
+               }
+               return componentNode;
        }
 
        private String createEvent(String name, ObjectNode parameters) {
@@ -224,5 +245,7 @@ public class Server implements Closeable {
        private final AtomicBoolean closed = new AtomicBoolean(false);
        private final Set<Consumer<String>> outputWriters = new HashSet<>();
        private final Map<String, Window> windows = new ConcurrentHashMap<>();
+       private final AtomicInteger idCounter = new AtomicInteger();
+       private final WeakHashMap<Component, Integer> componentIds = new WeakHashMap<>();
 
 }
index 780826f..95e6c9e 100644 (file)
@@ -1,6 +1,7 @@
 package de.qsheltier.msta;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.awt.BorderLayout;
 import java.awt.Frame;
 import java.awt.Window;
 import java.io.BufferedReader;
@@ -17,16 +18,23 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
 import static com.spotify.hamcrest.jackson.JsonMatchers.isJsonStringMatching;
-import static com.spotify.hamcrest.jackson.JsonMatchers.jsonBoolean;
-import static com.spotify.hamcrest.jackson.JsonMatchers.jsonMissing;
+import static com.spotify.hamcrest.jackson.JsonMatchers.jsonArray;
+import static com.spotify.hamcrest.jackson.JsonMatchers.jsonInt;
 import static com.spotify.hamcrest.jackson.JsonMatchers.jsonObject;
 import static com.spotify.hamcrest.jackson.JsonMatchers.jsonText;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anything;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.not;
 import static org.junit.jupiter.api.Timeout.ThreadMode.SEPARATE_THREAD;
@@ -185,19 +193,25 @@ public class ServerTest {
 
        @Test
        @Timeout(value = 5, unit = TimeUnit.SECONDS, threadMode = SEPARATE_THREAD)
-       public void windowInfoContainsButtonInfo() throws Throwable {
+       public void windowInfoContainsInfoAboutAllComponents() throws Throwable {
                connectToServer(verifyConnectedEvent((reader, ready, writer) -> {
                        var window = new Window(null);
-                       window.setVisible(true);
+                       SwingUtilities.invokeAndWait(() -> {
+                               var mainPanel = new JPanel(new BorderLayout());
+                               mainPanel.add(new JLabel("Label"), BorderLayout.NORTH);
+                               mainPanel.add(new JButton("Button"), BorderLayout.SOUTH);
+                               window.add(mainPanel);
+                               window.pack();
+                               window.setVisible(true);
+                       });
                        try {
                                var reply = objectMapper.readTree(reader.get());
-                               assertThat(reply, jsonObject().where("event", jsonText("window-opened")).where("id", not(jsonMissing())));
-                               var windowName = reply.get("id").asText();
-                               writer.accept("info window " + windowName);
-                               reply = objectMapper.readTree(reader.get());
-                               assertThat(reply, jsonObject()
-                                               .where("info", jsonText("window"))
-                                               .where("id", jsonText(windowName))
+                               System.out.println(reply);
+                               assertThat(reply, jsonObject().where("event", jsonText("window-opened")).where("id", jsonInt(anything()))
+                                               .where("children", jsonArray(contains(jsonObject().where("type", jsonText("javax.swing.JPanel"))
+                                                               .where("children", jsonArray(containsInAnyOrder(
+                                                                               jsonObject().where("type", jsonText("javax.swing.JLabel")),
+                                                                               jsonObject().where("type", jsonText("javax.swing.JButton"))))))))
                                );
                        } finally {
                                window.setVisible(false);