Files
JIMRI/java/test/jmri/jmrix/openlcb/OlcbTurnoutTest.java
T
2026-06-17 14:00:51 +02:00

405 lines
15 KiB
Java

package jmri.jmrix.openlcb;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.junit.Assert;
import org.junit.jupiter.api.*;
import org.openlcb.EventID;
import org.openlcb.implementations.EventTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jmri.Turnout;
import jmri.jmrix.can.CanMessage;
import jmri.util.JUnitUtil;
import jmri.util.NamedBeanComparator;
import jmri.util.PropertyChangeListenerScaffold;
import jmri.util.ThreadingUtil;
/**
* Tests for the jmri.jmrix.openlcb.OlcbTurnout class.
*
* @author Bob Jacobsen Copyright 2008, 2010, 2011
*/
public class OlcbTurnoutTest {
private static final Logger log = LoggerFactory.getLogger(OlcbTurnoutTest.class);
protected PropertyChangeListenerScaffold l;
@Test
public void testIncomingChange() {
Assert.assertNotNull("exists", t);
OlcbTurnout s = new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo);
s.finishLoad();
// message for Active and Inactive
CanMessage mActive = new CanMessage(
new int[]{1, 2, 3, 4, 5, 6, 7, 8},
0x195B4000
);
mActive.setExtended(true);
CanMessage mInactive = new CanMessage(
new int[]{1, 2, 3, 4, 5, 6, 7, 9},
0x195B4000
);
mInactive.setExtended(true);
s.addPropertyChangeListener(l);
// check states
Assert.assertEquals(Turnout.UNKNOWN, s.getCommandedState());
t.sendMessage(mActive);
JUnitUtil.waitFor( () -> l.getPropertyChanged(),"no prop change unknown > thrown");
Assert.assertEquals("called twice",2,l.getCallCount());
Assert.assertEquals(Turnout.THROWN, s.getCommandedState());
l.resetPropertyChanged();
t.sendMessage(mInactive);
JUnitUtil.waitFor( () -> l.getPropertyChanged(), "no prop change thrown > closed");
Assert.assertEquals("called twice",2,l.getCallCount());
Assert.assertEquals(Turnout.CLOSED, s.getCommandedState());
}
@Test
public void testLocalChange() {
// load dummy TrafficController
OlcbTurnout s = new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo);
s.finishLoad();
s.addPropertyChangeListener(l);
t.flush();
t.tc.rcvMessage = null;
s.setState(Turnout.THROWN);
t.flush();
JUnitUtil.waitFor( () -> l.getPropertyChanged(),"no prop change > thrown");
Assert.assertEquals("called twice",2,l.getCallCount());
Assert.assertEquals(Turnout.THROWN, s.getCommandedState());
log.debug("recv msg: {} header {}", t.tc.rcvMessage, Integer.toHexString(t.tc.rcvMessage.getHeader()));
Assert.assertTrue(new OlcbAddress("1.2.3.4.5.6.7.8", null).match(t.tc.rcvMessage));
l.resetPropertyChanged();
t.tc.rcvMessage = null;
s.setState(Turnout.CLOSED);
t.flush();
JUnitUtil.waitFor( () -> l.getPropertyChanged(), "no prop change thrown > closed");
Assert.assertEquals("called twice",2,l.getCallCount());
Assert.assertEquals(Turnout.CLOSED, s.getCommandedState());
Assert.assertTrue(new OlcbAddress("1.2.3.4.5.6.7.9", null).match(t.tc.rcvMessage));
// repeated set of local state
t.tc.rcvMessage = null;
s.setState(Turnout.CLOSED);
t.flush();
Assert.assertTrue(l.getPropertyChanged());
Assert.assertEquals("called twice",2,l.getCallCount());
Assert.assertEquals(Turnout.CLOSED, s.getCommandedState());
Assert.assertTrue(new OlcbAddress("1.2.3.4.5.6.7.9", null).match(t.tc.rcvMessage));
}
@Test
public void testAuthoritative() {
OlcbTurnout s = new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo);
s.setFeedbackMode(Turnout.MONITORING);
s.finishLoad();
s.setState(Turnout.THROWN);
t.flush();
// message for Active and Inactive
CanMessage qActive = new CanMessage(new int[]{1, 2, 3, 4, 5, 6, 7, 8},
0x19914123
);
qActive.setExtended(true);
t.sendMessage(qActive);
t.flush();
CanMessage expected = new CanMessage(new int[]{1, 2, 3, 4, 5, 6, 7, 8},
0x19544c4c);
expected.setExtended(true);
Assert.assertEquals(expected, t.tc.rcvMessage);
t.tc.rcvMessage = null;
s.setAuthoritative(false);
s.setState(Turnout.CLOSED);
t.flush();
t.sendMessage(qActive);
t.flush();
expected = new CanMessage(new int[]{1, 2, 3, 4, 5, 6, 7, 8},
0x19547c4c);
expected.setExtended(true);
Assert.assertEquals(expected, t.tc.rcvMessage);
}
@Test
public void testLoopback() {
// Two turnouts behaving in opposite ways. One will be used to generate an event and the
// other will be observed to make sure it catches it.
OlcbTurnout s = new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo);
s.finishLoad();
OlcbTurnout r = new OlcbTurnout("M", "1.2.3.4.5.6.7.9;1.2.3.4.5.6.7.8", t.systemConnectionMemo);
r.finishLoad();
r.addPropertyChangeListener(l);
s.setState(Turnout.THROWN);
t.flush();
JUnitUtil.waitFor( () -> l.getPropertyChanged(),"prop change > thrown");
Assert.assertEquals("called twice",2,l.getCallCount());
Assert.assertEquals(Turnout.THROWN, s.getCommandedState());
l.resetPropertyChanged();
s.setState(Turnout.CLOSED);
t.flush();
JUnitUtil.waitFor( () -> l.getPropertyChanged(),"prop change > closed");
Assert.assertEquals("called twice",2,l.getCallCount());
Assert.assertEquals(Turnout.CLOSED, s.getCommandedState());
}
@Test
public void testForgetState() {
OlcbTurnout s = new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo);
s.setProperty(OlcbUtils.PROPERTY_LISTEN, Boolean.FALSE.toString());
s.finishLoad();
t.sendMessageAndExpectResponse(":X19914123N0102030405060708;",
":X19547C4CN0102030405060708;");
Assert.assertEquals(Turnout.MONITORING, s.getFeedbackMode());
s.setState(Turnout.THROWN);
t.flush();
Assert.assertEquals(Turnout.THROWN, s.getCommandedState());
Assert.assertEquals(Turnout.THROWN, s.getKnownState());
t.assertSentMessage(":X195B4c4cN0102030405060708;");
s.addPropertyChangeListener(l);
t.sendMessageAndExpectResponse(":X19914123N0102030405060708;",
":X19544C4CN0102030405060708;");
// Getting a state notify will not change state now.
t.sendMessage(":X19544123N0102030405060709;");
Assert.assertEquals("not called",0,l.getCallCount());
l.resetPropertyChanged();
Assert.assertEquals(Turnout.THROWN, s.getKnownState());
// Resets the turnout to unknown state
s.setState(Turnout.UNKNOWN);
JUnitUtil.waitFor( () -> l.getPropertyChanged(), "prop changed > unknown");
Assert.assertEquals("called twice",2,l.getCallCount());
l.resetPropertyChanged();
t.assertNoSentMessages();
// state is reported as unknown to the bus
t.sendMessageAndExpectResponse(":X19914123N0102030405060708;",
":X19547C4CN0102030405060708;");
// getting a state notify will change state
t.sendMessage(":X19544123N0102030405060709;");
JUnitUtil.waitFor( () -> l.getPropertyChanged(), "prop change > closed");
Assert.assertEquals("called twice",2,l.getCallCount());
l.resetPropertyChanged();
Assert.assertEquals(Turnout.CLOSED, s.getKnownState());
// state is reported as known (thrown==invalid)
t.sendMessageAndExpectResponse(":X19914123N0102030405060708;",
":X19545C4CN0102030405060708;");
// getting a state notify will not change state
t.sendMessage(":X19544123N0102030405060708;");
Assert.assertEquals("not called",0,l.getCallCount());
l.resetPropertyChanged();
Assert.assertEquals(Turnout.CLOSED, s.getKnownState());
}
@Test
public void testQueryState() {
OlcbTurnout s = new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo);
s.finishLoad();
t.tc.rcvMessage = null;
s.requestUpdateFromLayout();
t.flush();
t.assertSentMessage(":X198F4C4CN0102030405060708;");
s.setFeedbackMode(Turnout.DIRECT);
t.flush();
t.tc.rcvMessage = null;
s.requestUpdateFromLayout();
t.flush();
t.assertNoSentMessages();
}
/**
* In this test we simulate the following scenario: A turnout R that is being changed locally
* by JMRI (e.g. due to a panel icon action), which triggers a Logix, and in that Logix there
* is an action that sets a second turnout U.
* We check that the messages sent to the layout are in the order of T:=Active, U:=Active.
*/
@Test
public void testListenerOutOfOrder() {
final OlcbTurnout r = new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo);
final OlcbTurnout u = new OlcbTurnout("M", "1.2.3.4.5.6.7.a;1.2.3.4.5.6.7.b", t.systemConnectionMemo);
r.finishLoad();
u.finishLoad();
r.setCommandedState(Turnout.CLOSED);
u.setCommandedState(Turnout.CLOSED);
t.clearSentMessages();
r.addPropertyChangeListener("KnownState", propertyChangeEvent -> {
Assert.assertEquals(Turnout.THROWN, r.getKnownState());
u.setCommandedState(Turnout.THROWN);
});
ThreadingUtil.runOnLayout(() -> r.setCommandedState(Turnout.THROWN));
Assert.assertEquals(Turnout.THROWN, r.getKnownState());
Assert.assertEquals(Turnout.THROWN, u.getKnownState());
// Ensures that the last sent message is U==Active. Particularly important that it is NOT
// the message ending with 0708.
t.assertSentMessage(":X195B4C4CN010203040506070A;");
}
@Test
public void testEventTable() {
OlcbTurnout s = new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo);
s.finishLoad();
EventTable.EventTableEntry[] elist = t.iface.getEventTable()
.getEventInfo(new EventID("1.2.3.4.5.6.7.8")).getAllEntries();
Assert.assertEquals(1, elist.length);
Assert.assertTrue("Incorrect name: " + elist[0].getDescription(), Pattern.compile("Turnout.*Thrown").matcher(elist[0].getDescription()).matches());
s.setUserName("MySwitch");
elist = t.iface.getEventTable()
.getEventInfo(new EventID("1.2.3.4.5.6.7.8")).getAllEntries();
Assert.assertEquals(1, elist.length);
Assert.assertEquals("Turnout MySwitch Thrown", elist[0].getDescription());
elist = t.iface.getEventTable()
.getEventInfo(new EventID("1.2.3.4.5.6.7.9")).getAllEntries();
Assert.assertEquals(1, elist.length);
Assert.assertEquals("Turnout MySwitch Closed", elist[0].getDescription());
}
@Test
public void testNameFormatXlower() {
// load dummy TrafficController
OlcbTurnout s = new OlcbTurnout("M", "x0501010114FF2000;x0501010114FF2001", t.systemConnectionMemo);
s.finishLoad();
Assert.assertNotNull("to exists", s);
// message for Active and Inactive
CanMessage mActive = new CanMessage(
new int[]{0x05, 0x01, 0x01, 0x01, 0x14, 0xFF, 0x20, 0x00},
0x195B4000
);
mActive.setExtended(true);
CanMessage mInactive = new CanMessage(
new int[]{0x05, 0x01, 0x01, 0x01, 0x14, 0xFF, 0x20, 0x01},
0x195B4000
);
mInactive.setExtended(true);
// check states
Assert.assertEquals(Turnout.UNKNOWN, s.getCommandedState());
t.sendMessage(mActive);
Assert.assertEquals(Turnout.THROWN, s.getCommandedState());
t.sendMessage(mInactive);
Assert.assertEquals(Turnout.CLOSED, s.getCommandedState());
}
@Test
public void testNameFormatXupper() {
// load dummy TrafficController
OlcbTurnout s = new OlcbTurnout("M", "X0501010114FF2000;X0501010114FF2001", t.systemConnectionMemo);
s.finishLoad();
Assert.assertNotNull("to exists", s);
// message for Active and Inactive
CanMessage mActive = new CanMessage(
new int[]{0x05, 0x01, 0x01, 0x01, 0x14, 0xFF, 0x20, 0x00},
0x195B4000
);
mActive.setExtended(true);
CanMessage mInactive = new CanMessage(
new int[]{0x05, 0x01, 0x01, 0x01, 0x14, 0xFF, 0x20, 0x01},
0x195B4000
);
mInactive.setExtended(true);
// check states
Assert.assertEquals(Turnout.UNKNOWN, s.getCommandedState());
t.sendMessage(mActive);
Assert.assertEquals(Turnout.THROWN, s.getCommandedState());
t.sendMessage(mInactive);
Assert.assertEquals(Turnout.CLOSED, s.getCommandedState());
}
@Test
public void testSystemSpecificComparisonOfSpecificFormats() {
// test by putting into a tree set, then extracting and checking order
TreeSet<Turnout> set = new TreeSet<>(new NamedBeanComparator<>());
set.add(new OlcbTurnout("M", "1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", t.systemConnectionMemo));
set.add(new OlcbTurnout("M", "X0501010114FF2000;X0501010114FF2011", t.systemConnectionMemo));
set.add(new OlcbTurnout("M", "X0501010114FF2000;X0501010114FF2001", t.systemConnectionMemo));
set.add(new OlcbTurnout("M", "1.2.3.4.5.6.7.9;1.2.3.4.5.6.7.9", t.systemConnectionMemo));
Iterator<Turnout> it = set.iterator();
Assert.assertEquals("MT1.2.3.4.5.6.7.8;1.2.3.4.5.6.7.9", it.next().getSystemName());
Assert.assertEquals("MT1.2.3.4.5.6.7.9;1.2.3.4.5.6.7.9", it.next().getSystemName());
Assert.assertEquals("MTX0501010114FF2000;X0501010114FF2001", it.next().getSystemName());
Assert.assertEquals("MTX0501010114FF2000;X0501010114FF2011", it.next().getSystemName());
}
private OlcbTestInterface t;
@BeforeAll
public static void checkSeparate() {
// this test is run separately because it leaves a lot of threads behind
org.junit.Assume.assumeFalse("Ignoring intermittent test", Boolean.getBoolean("jmri.skipTestsRequiringSeparateRunning"));
}
@BeforeEach
public void setUp() {
JUnitUtil.setUp();
l = new PropertyChangeListenerScaffold();
// load dummy TrafficController
t = new OlcbTestInterface(new OlcbTestInterface.CreateConfigurationManager());
t.waitForStartup();
}
@AfterEach
public void tearDown() {
l = null;
t.dispose();
t = null;
JUnitUtil.clearShutDownManager(); // put in place because AbstractMRTrafficController implementing subclass was not terminated properly
JUnitUtil.tearDown();
}
}