Files
2026-06-17 14:00:51 +02:00

421 lines
16 KiB
Java

package jmri.jmrit.blockboss;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import jmri.InstanceManager;
import jmri.Sensor;
import jmri.SignalHead;
import jmri.util.JUnitUtil;
import org.junit.jupiter.api.*;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for the BlockBossLogic class
*
* @author Bob Jacobsen
*/
public class BlockBossLogicTest {
// test creation
@Test
public void testCreate() {
p = new BlockBossLogic("IH2");
assertThat(p.getDrivenSignal()).withFailMessage("driven signal name").isEqualTo("IH2");
}
// test simplest block, just signal following
@Test
public void testSimpleBlock() {
setupSimpleBlock();
startLogic();
assertThat(p.getDrivenSignal()).withFailMessage("driven signal name").isEqualTo("IH1");
JUnitUtil.setBeanStateAndWait(h2, SignalHead.YELLOW);
JUnitUtil.waitFor(()-> SignalHead.GREEN == h1.getAppearance(), "Stuck at "+h1.getAppearance()+" so yellow sets green"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.RED);
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "Stuck at "+h1.getAppearance()+" so red sets yellow"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.GREEN);
JUnitUtil.waitFor(()-> SignalHead.GREEN == h1.getAppearance(), "Stuck at "+h1.getAppearance()+" so green sets green"); // wait and test
}
// test that initial conditions are set right
@Test
public void testSimpleBlockInitial() {
setupSimpleBlock();
startLogic();
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "initial red sets yellow"); // wait and test
}
// occupancy check
@Test
public void testSimpleBlockOccupancy() {
setupSimpleBlock();
p.setSensor1("IS1");
startLogic();
JUnitUtil.setBeanState(s1, Sensor.INACTIVE);
JUnitUtil.setBeanStateAndWait(h2, SignalHead.YELLOW);
JUnitUtil.waitFor(()-> SignalHead.GREEN == h1.getAppearance(), "Stuck at "+h1.getAppearance()+" so yellow sets green"); // wait and test
JUnitUtil.setBeanState(s1, Sensor.ACTIVE);
JUnitUtil.waitFor(()-> SignalHead.RED == h1.getAppearance(), "Stuck at "+h1.getAppearance()+" so occupied sets red"); // wait and test
JUnitUtil.setBeanState(s1, Sensor.INACTIVE);
JUnitUtil.waitFor(()-> SignalHead.GREEN == h1.getAppearance(), "Stuck at "+h1.getAppearance()+" so unoccupied sets green"); // wait and test
}
// test signal following in distant simple block
@Test
public void testSimpleBlockDistant() {
setupSimpleBlock();
p.setDistantSignal(true);
startLogic();
assertThat(p.getDrivenSignal()).withFailMessage("driven signal name").isEqualTo("IH1");
JUnitUtil.setBeanStateAndWait(h2, SignalHead.YELLOW);
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "yellow sets yellow"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.RED);
JUnitUtil.waitFor(()-> SignalHead.RED == h1.getAppearance(), "red sets red"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.GREEN);
JUnitUtil.waitFor(()-> SignalHead.GREEN == h1.getAppearance(), "green sets green"); // wait and test
}
// test signal following in limited simple block
// (not particularly interesting, as next signal can't set red)
@Test
public void testSimpleBlockLimited() {
setupSimpleBlock();
p.setLimitSpeed1(true);
startLogic();
JUnitUtil.setBeanStateAndWait(h2, SignalHead.RED);
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "red sets yellow"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.YELLOW);
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "yellow sets yellow"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.GREEN);
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "green sets yellow"); // wait and test
}
// test signal following in distant, limited simple block
@Test
public void testSimpleBlockDistantLimited() {
setupSimpleBlock();
p.setDistantSignal(true);
p.setLimitSpeed1(true);
startLogic();
JUnitUtil.setBeanStateAndWait(h2, SignalHead.YELLOW);
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "yellow sets yellow"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.RED);
JUnitUtil.waitFor(()-> SignalHead.RED == h1.getAppearance(), "red sets red"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.GREEN);
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "green sets yellow"); // wait and test
}
// test signal following in restricting simple block
@Test
public void testSimpleBlockRestricting() {
JUnitUtil.setBeanState(s1, Sensor.INACTIVE);
setupSimpleBlock();
p.setSensor1("IS1");
p.setRestrictingSpeed1(true);
startLogic();
JUnitUtil.setBeanStateAndWait(h2, SignalHead.YELLOW);
JUnitUtil.waitFor(()-> SignalHead.FLASHRED == h1.getAppearance(), "yellow sets flashing red"); // wait and test
JUnitUtil.setBeanState(s1, Sensor.ACTIVE);
JUnitUtil.waitFor(()-> SignalHead.RED == h1.getAppearance(), "Stuck at "+h1.getAppearance()+" so occupied sets red"); // wait and test
JUnitUtil.setBeanStateAndWait(h2, SignalHead.GREEN);
JUnitUtil.setBeanState(s1, Sensor.INACTIVE);
JUnitUtil.waitFor(()-> SignalHead.FLASHRED == h1.getAppearance(), "Stuck at "+h1.getAppearance()+" so unoccupied green sets flashing red"); // wait and test
}
// if no next signal, next signal considered green
@Test
public void testSimpleBlockNoNext() throws jmri.JmriException {
s1.setState(Sensor.INACTIVE);
p = new BlockBossLogic("IH1");
p.setSensor1("1");
p.setMode(BlockBossLogic.SINGLEBLOCK);
startLogic();
JUnitUtil.waitFor(()-> SignalHead.GREEN == h1.getAppearance(), "missing signal is green"); // wait and test
}
// if no next signal, next signal is considered green
@Test
public void testSimpleBlockNoNextLimited() throws jmri.JmriException {
s1.setState(Sensor.INACTIVE);
p = new BlockBossLogic("IH1");
p.setMode(BlockBossLogic.SINGLEBLOCK);
p.setSensor1("1");
p.setLimitSpeed1(true);
startLogic();
JUnitUtil.waitFor(()-> SignalHead.YELLOW == h1.getAppearance(), "missing signal is green, show yellow"); // wait and test
}
// check for basic not-fail if no signal name was set
@Test
public void testSimpleBlockNoSignal() {
Exception ex = Assertions.assertThrows(NullPointerException.class, () -> {
var bbl = getThrowNpeBlockBossLogic();
Assertions.fail("Should have thrown NPE on line above, not created " + bbl);
});
Assertions.assertNotNull(ex);
Assertions.assertEquals("BlockBossLogic name cannot be null", ex.getMessage());
}
@SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") // passing null to test exception
private BlockBossLogic getThrowNpeBlockBossLogic() {
return new BlockBossLogic(null);
}
// check for basic not-fail if empty signal name was set
@Test
public void testSimpleBlockEmptyName() {
Exception ex = Assertions.assertThrows(IllegalArgumentException.class, () -> {
Assertions.assertNull(new BlockBossLogic(""));});
Assertions.assertNotNull(ex);
Assertions.assertEquals("SignalHead \"\" does not exist", ex.getMessage());
jmri.util.JUnitAppender.assertWarnMessage("Signal Head \"\" was not found");
}
// test interruption
@Test
public void testInterrupt() throws jmri.JmriException {
s1.setState(Sensor.INACTIVE);
forceInterrupt = false;
p = new BlockBossLogic("IH1") {
@Override
public void setOutput() {
testThread = this.thread;
if (forceInterrupt) {
testThread.interrupt(); // force an interrupt of the SSL thread
}
super.setOutput();
}
};
p.setMode(BlockBossLogic.SINGLEBLOCK);
p.setSensor1("1");
p.setLimitSpeed1(true);
startLogic();
JUnitUtil.waitFor(()-> p.isRunning(), "is running");
forceInterrupt = true;
s1.setState(Sensor.ACTIVE);
JUnitUtil.waitFor(()-> !p.isRunning(), "is stopped");
}
// check that user names were preserved
@Test
public void testUserNamesRetained() {
p = new BlockBossLogic("IH1");
p.setSensor1("1");
p.setSensor2("2");
p.setSensor3("3");
p.setSensor4("4");
p.setSensor5("10");
p.setTurnout("1");
p.setWatchedSignal1("1", false);
p.setWatchedSignal1Alt("2");
p.setWatchedSignal2("3");
p.setWatchedSignal2Alt("4");
p.setWatchedSensor1("5");
p.setWatchedSensor1Alt("6");
p.setWatchedSensor2("7");
p.setWatchedSensor2Alt("8");
p.setApproachSensor1("9");
assertThat(p.getSensor1()).withFailMessage("sensor1").isEqualTo("1");
assertThat(p.getSensor2()).withFailMessage("sensor2").isEqualTo("2");
assertThat(p.getSensor3()).withFailMessage("sensor3").isEqualTo("3");
assertThat(p.getSensor4()).withFailMessage("sensor4").isEqualTo("4");
assertThat(p.getSensor5()).withFailMessage("sensor5").isEqualTo("10");
assertThat(p.getTurnout()).withFailMessage("turnout1").isEqualTo("1");
assertThat(p.getWatchedSignal1()).withFailMessage("watchedsignal1").isEqualTo("1");
assertThat(p.getWatchedSignal1Alt()).withFailMessage("watchedsignal1alt").isEqualTo("2");
assertThat(p.getWatchedSignal2()).withFailMessage("watchedsignal2").isEqualTo("3");
assertThat(p.getWatchedSignal2Alt()).withFailMessage("watchedsignal2alt").isEqualTo("4");
assertThat(p.getWatchedSensor1()).withFailMessage("watchedsensor1").isEqualTo("5");
assertThat(p.getWatchedSensor1Alt()).withFailMessage("watchedsensor1alt").isEqualTo("6");
assertThat(p.getWatchedSensor2()).withFailMessage("watchedsensor2").isEqualTo("7");
assertThat(p.getWatchedSensor2Alt()).withFailMessage("watchedsensor2alt").isEqualTo("8");
assertThat(p.getApproachSensor1()).withFailMessage("approach").isEqualTo("9");
}
// check that system names were preserved
@Test
public void testSystemNamesRetained() {
p = new BlockBossLogic("IH1");
p.setSensor1("IS1");
p.setSensor2("IS2");
p.setSensor3("IS3");
p.setSensor4("IS4");
p.setSensor5("IS10");
p.setTurnout("IT1");
p.setWatchedSignal1("IH1", false);
p.setWatchedSignal1Alt("IH2");
p.setWatchedSignal2("IH3");
p.setWatchedSignal2Alt("IH4");
p.setWatchedSensor1("IS5");
p.setWatchedSensor1Alt("IS6");
p.setWatchedSensor2("IS7");
p.setWatchedSensor2Alt("IS8");
p.setApproachSensor1("IS9");
assertThat(p.getSensor1()).withFailMessage("sensor1").isEqualTo("IS1");
assertThat(p.getSensor2()).withFailMessage("sensor2").isEqualTo("IS2");
assertThat(p.getSensor3()).withFailMessage("sensor3").isEqualTo("IS3");
assertThat(p.getSensor4()).withFailMessage("sensor4").isEqualTo("IS4");
assertThat(p.getSensor5()).withFailMessage("sensor5").isEqualTo("IS10");
assertThat(p.getTurnout()).withFailMessage("turnout1").isEqualTo("IT1");
assertThat(p.getWatchedSignal1()).withFailMessage("watchedsignal1").isEqualTo("IH1");
assertThat(p.getWatchedSignal1Alt()).withFailMessage("watchedsignal1alt").isEqualTo("IH2");
assertThat(p.getWatchedSignal2()).withFailMessage("watchedsignal2").isEqualTo("IH3");
assertThat(p.getWatchedSignal2Alt()).withFailMessage("watchedsignal2alt").isEqualTo("IH4");
assertThat(p.getWatchedSensor1()).withFailMessage("watchedsensor1").isEqualTo("IS5");
assertThat(p.getWatchedSensor1Alt()).withFailMessage("watchedsensor1alt").isEqualTo("IS6");
assertThat(p.getWatchedSensor2()).withFailMessage("watchedsensor2").isEqualTo("IS7");
assertThat(p.getWatchedSensor2Alt()).withFailMessage("watchedsensor2alt").isEqualTo("IS8");
assertThat(p.getApproachSensor1()).withFailMessage("approach").isEqualTo("IS9");
}
// Turnout t1, t2, t3;
private Sensor s1; //, s2, s3, s4, s5, s6, s7, s8, s9, s10;
private SignalHead h1, h2, h3, h4;
private BlockBossLogic p;
private Thread testThread = null;
private boolean forceInterrupt = false;
protected void startLogic() {
if (p != null) {
p.start();
}
}
protected void stopLogic() {
if (p != null) {
p.stop();
}
}
void setupSimpleBlock() {
p = new BlockBossLogic("IH1");
p.setMode(BlockBossLogic.SINGLEBLOCK);
p.setWatchedSignal1("IH2", false);
}
/**
* Test-by test initialization. Does log4j for standalone use, and then
* creates a set of turnouts, sensors and signals as common background for
* testing
*/
@BeforeEach
public void setUp() {
// reset InstanceManager
JUnitUtil.setUp();
JUnitUtil.initInternalSensorManager();
JUnitUtil.initInternalTurnoutManager();
JUnitUtil.initInternalSignalHeadManager();
// clear the BlockBossLogic static list
ArrayList<SignalHead> heads = new ArrayList<>();
for (BlockBossLogic b : InstanceManager.getDefault(BlockBossLogicProvider.class).provideAll()) {
heads.add(b.getDrivenSignalNamedBean().getBean());
}
for (SignalHead head : heads) { // avoids ConcurrentModificationException
BlockBossLogic.getStoppedObject(head);
}
// t1 = InstanceManager.turnoutManagerInstance().newTurnout("IT1", "1");
// t2 = InstanceManager.turnoutManagerInstance().newTurnout("IT2", "2");
// t3 = InstanceManager.turnoutManagerInstance().newTurnout("IT3", "3");
s1 = InstanceManager.sensorManagerInstance().newSensor("IS1", "1");
// s2 = InstanceManager.sensorManagerInstance().newSensor("IS2", "2");
// s3 = InstanceManager.sensorManagerInstance().newSensor("IS3", "3");
// s4 = InstanceManager.sensorManagerInstance().newSensor("IS4", "4");
// s5 = InstanceManager.sensorManagerInstance().newSensor("IS5", "5");
// s6 = InstanceManager.sensorManagerInstance().newSensor("IS6", "6");
// s7 = InstanceManager.sensorManagerInstance().newSensor("IS7", "7");
// s8 = InstanceManager.sensorManagerInstance().newSensor("IS8", "8");
// s9 = InstanceManager.sensorManagerInstance().newSensor("IS9", "9");
// s10 = InstanceManager.sensorManagerInstance().newSensor("IS10", "10");
h1 = new jmri.implementation.VirtualSignalHead("IH1", "1");
InstanceManager.getDefault(jmri.SignalHeadManager.class).register(h1);
JUnitUtil.setBeanStateAndWait(h1, SignalHead.RED); // ensure starting point
h2 = new jmri.implementation.VirtualSignalHead("IH2", "2");
InstanceManager.getDefault(jmri.SignalHeadManager.class).register(h2);
JUnitUtil.setBeanStateAndWait(h2, SignalHead.RED); // ensure starting point
h3 = new jmri.implementation.VirtualSignalHead("IH3", "3");
InstanceManager.getDefault(jmri.SignalHeadManager.class).register(h3);
JUnitUtil.setBeanStateAndWait(h3, SignalHead.RED); // ensure starting point
h4 = new jmri.implementation.VirtualSignalHead("IH4", "4");
InstanceManager.getDefault(jmri.SignalHeadManager.class).register(h4);
JUnitUtil.setBeanStateAndWait(h4, SignalHead.RED); // ensure starting point
}
@AfterEach
public void tearDown() {
// t1=t2=t3=null;
s1=null; // s2=s3=s4=s5=s6=s7=s8=s9=s10=null;
h1=h2=h3=h4=null;
testThread = null;
stopLogic();
// reset InstanceManager
JUnitUtil.tearDown();
}
}