405 lines
15 KiB
Java
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();
|
|
|
|
}
|
|
}
|