Files
JIMRI/java/test/jmri/web/servlet/panel/ControlPanelServletTest.java
T
2026-06-17 14:00:51 +02:00

193 lines
6.8 KiB
Java

package jmri.web.servlet.panel;
import java.io.StringReader;
import java.util.List;
import jmri.InstanceManager;
import jmri.configurexml.ConfigXmlManager;
import jmri.jmrit.display.Editor;
import jmri.jmrit.display.PositionableLabel;
import jmri.jmrit.display.IndicatorTrackIcon;
import jmri.jmrit.display.controlPanelEditor.ControlPanelEditor;
import jmri.jmrit.logix.OBlock;
import jmri.jmrit.logix.OBlockManager;
import jmri.util.JUnitUtil;
import jmri.util.junit.annotations.DisabledIfHeadless;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Tests for the jmri.web.servlet.panel.ControlPanelServlet class
*
* @author Paul Bender Copyright (C) 2012,2016
* @author Egbert Broerse Copyright (C) 2020
*/
public class ControlPanelServletTest {
@Test
public void testCtor() {
ControlPanelServlet a = new ControlPanelServlet();
assertNotNull(a);
}
@Test
public void testIndicatorTrackIconElement() {
String systemName = "OB1";
String userName = "Internal OBlock 1";
ControlPanelServlet servlet = new NullControlPanelServlet();
OBlock ob = InstanceManager.getDefault(OBlockManager.class).provide(systemName);
ob.setUserName(userName);
IndicatorTrackIcon iti = new IndicatorTrackIcon(null);
iti.setOccBlock(ob.getUserName());
Element e = ConfigXmlManager.elementFromObject(iti);
assertEquals(userName, e.getChild("occupancyblock").getValue());
e = servlet.positionableElement(iti);
assertEquals(userName, e.getChild("occupancyblock").getValue());
//System.out.println(e.getChild("oblocksysname").toString()); // servlet is not adding anything to e
//assertEquals(systemName, e.getChild("oblocksysname").getValue()); / Child (element) not found, NPE
}
/**
* The served Control Panel XML must list positionables in ascending display
* level (z-order) so the web client, which draws elements in document order,
* reproduces the desktop stacking order. See JMRI/JMRI#12794.
*/
@Test
@DisabledIfHeadless
public void testContentsSortedByDisplayLevel() throws Exception {
String panelName = "CP Level Order Test";
ControlPanelEditor editor = new ControlPanelEditor(panelName);
try {
// add in reverse z-order (highest level first) on purpose
addLabel(editor, "high", Editor.MARKERS); // level 10
addLabel(editor, "low", Editor.TURNOUTS); // level 7
List<Element> labels = positionableLabels(
new EditorBoundServlet(editor, panelName).getXmlPanel(panelName));
int idxLow = indexOfLabel(labels, "low");
int idxHigh = indexOfLabel(labels, "high");
assertTrue(idxLow >= 0 && idxHigh >= 0, "both labels emitted");
assertTrue(idxLow < idxHigh,
"lower display level must be emitted before higher display level");
// the emitted level attributes themselves must be non-decreasing
int prev = Integer.MIN_VALUE;
for (Element label : labels) {
int level = Integer.parseInt(label.getAttributeValue("level"));
assertTrue(level >= prev, "emitted levels must be non-decreasing");
prev = level;
}
} finally {
JUnitUtil.dispose(editor);
}
}
/**
* Two positionables sharing the same display level must keep their original
* insertion order. Guards against an accidental unstable sort.
*/
@Test
@DisabledIfHeadless
public void testSameDisplayLevelStable() throws Exception {
String panelName = "CP Level Stability Test";
ControlPanelEditor editor = new ControlPanelEditor(panelName);
try {
addLabel(editor, "first", Editor.LABELS); // level 4
addLabel(editor, "second", Editor.LABELS); // level 4
List<Element> labels = positionableLabels(
new EditorBoundServlet(editor, panelName).getXmlPanel(panelName));
int idxFirst = indexOfLabel(labels, "first");
int idxSecond = indexOfLabel(labels, "second");
assertTrue(idxFirst >= 0 && idxSecond >= 0, "both labels emitted");
assertTrue(idxFirst < idxSecond,
"same-level elements must retain insertion order (stable sort)");
} finally {
JUnitUtil.dispose(editor);
}
}
private void addLabel(Editor editor, String text, int level) throws Exception {
PositionableLabel l = new PositionableLabel(text, editor);
editor.putItem(l);
l.setDisplayLevel(level);
}
private List<Element> positionableLabels(String xml) throws Exception {
assertNotNull(xml, "panel xml produced");
Element panel = new SAXBuilder().build(new StringReader(xml)).getRootElement();
return panel.getChildren("positionablelabel");
}
private int indexOfLabel(List<Element> labels, String text) {
for (int i = 0; i < labels.size(); i++) {
if (text.equals(labels.get(i).getAttributeValue("text"))) {
return i;
}
}
return -1;
}
@BeforeEach
public void setUp() {
JUnitUtil.setUp();
}
@AfterEach
public void tearDown() {
// ControlPanelEditor instances created by the z-order tests register a
// BlockManager shutdown task; deregister it so the harness tearDown check
// does not flag a leftover shutdown callable.
JUnitUtil.deregisterBlockManagerShutdownTask();
JUnitUtil.tearDown();
}
private static class NullControlPanelServlet extends ControlPanelServlet {
@Override
protected String getPanelType() {
return "ControlPanel";
}
@Override
protected String getJsonPanel(String name) {
return null;
}
@Override
protected String getXmlPanel(String name) {
return null;
}
}
/**
* Test servlet that resolves {@code getEditor(name)} to a known editor so
* {@link ControlPanelServlet#getXmlPanel(String)} can be exercised directly
* without depending on frame-title lookup timing.
*/
private static class EditorBoundServlet extends ControlPanelServlet {
private final Editor boundEditor;
private final String boundName;
EditorBoundServlet(Editor editor, String name) {
this.boundEditor = editor;
this.boundName = name;
}
@Override
protected Editor getEditor(String n) {
return boundName.equals(n) ? boundEditor : super.getEditor(n);
}
}
}