package jmri.jmrix.dccpp; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import jmri.*; import jmri.util.JUnitUtil; import org.junit.jupiter.api.*; /** * DCCppProgrammerTest.java * * JUnit tests for the DCCppProgrammer class * * @author Bob Jacobsen * @author Mark Underwood (C) 2015 */ public class DCCppProgrammerTest extends jmri.jmrix.AbstractProgrammerTest { static final int RESTART_TIME = 20; private DCCppInterfaceScaffold t = null; private ProgListenerScaffold l = null; private DCCppProgrammer p = null; @Test @Override public void testDefault() { assertEquals( ProgrammingMode.DIRECTBYTEMODE, programmer.getMode(), "Check Default"); } @Override @Test public void testDefaultViaBestMode() { assertEquals( ProgrammingMode.DIRECTBYTEMODE, ((DCCppProgrammer) programmer).getBestMode(), "Check Default"); } @Test @Override public void testSetGetMode() { Throwable throwable = assertThrows( IllegalArgumentException.class, () -> programmer.setMode(ProgrammingMode.REGISTERMODE)); assertNotNull(throwable.getMessage()); } @Override @Test public void testGetCanWriteAddress() { assertFalse( programmer.getCanWrite("1234"), "can write address"); } @Override @Test public void testGetWriteConfirmMode() { assertEquals( Programmer.WriteConfirmMode.DecoderReply, programmer.getWriteConfirmMode("1234"), "Write Confirm Mode"); } @Test public void testWriteCvSequence() throws JmriException { // and do the write p.writeCV("29", 34, l); // check "prog mode" message sent assertEquals( 1, t.outbound.size(), "mode message sent"); assertEquals( "W 29 34 0 87", t.outbound.elementAt(0).toString(), "write message contents"); // send reply DCCppReply mr1 = DCCppReply.parseDCCppReply("r 0|87|29 34"); t.sendTestMessage(mr1); // At this point, the standard DCC++ programmer // should send a result to the programmer listeners, and // wait for either the next read/write request or for the // traffic controller to exit from service mode. We just // need to wait a few seconds and see that the listener we // registered earlier received the values we expected. JUnitUtil.waitFor(() -> !(l.getRcvdInvoked() == 0), "Rcvd not invoked"); assertNotEquals( 0, l.getRcvdInvoked(), "Receive Called by Programmer"); assertEquals( 34, l.getRcvdValue(), "Direct mode received value"); } @Test @Disabled("test body is commented out") public void testWriteRegisterSequence() throws JmriException { /* // infrastructure objects DCCppInterfaceScaffold t = new DCCppInterfaceScaffold(new DCCppCommandStation()); jmri.ProgListenerScaffold l = new jmri.ProgListenerScaffold(); DCCppProgrammer p = new DCCppProgrammer(t) { protected synchronized void restartTimer(int delay) { super.restartTimer(RESTART_TIME); } }; // set register mode p.setMode(ProgrammingMode.REGISTERMODE); // and do the write p.writeCV("29", 12, l); // check "prog mode" message sent Assert.assertEquals("read message sent", 1, t.outbound.size()); Assert.assertEquals("write message contents", "23 12 05 0C 38", t.outbound.elementAt(0).toString()); // send reply DCCppReply mr1 = new DCCppReply(); mr1.setElement(0, 0x61); mr1.setElement(1, 0x02); mr1.setElement(2, 0x63); t.sendTestMessage(mr1); Assert.assertEquals("inquire message sent", 2, t.outbound.size()); Assert.assertEquals("inquire message contents", "21 10 31", t.outbound.elementAt(1).toString()); // send a result string DCCppReply mr2 = new DCCppReply(); mr2.setElement(0, 0x63); mr2.setElement(1, 0x10); mr2.setElement(2, 0x05); mr2.setElement(3, 0x0C); mr2.setElement(4, 0x7A); t.sendTestMessage(mr2); // At this point, the standard XpressNet programmer // should send a result to the programmer listeners, and // wait for either the next read/write request or for the // traffic controller to exit from service mode. We just // need to wait a few seconds and see that the listener we // registered earlier received the values we expected. JUnitUtil.waitFor(() -> { return !(l.getRcvdInvoked() == 0); }); //failure in this test occurs with the next line. Assert.assertFalse("Receive Called by Programmer", l.getRcvdInvoked() == 0); Assert.assertEquals("Register mode received value", 12, l.getRcvdValue()); */ } @Test public void testReadCvSequence() throws JmriException { // and do the read p.readCV("29", l); // check "prog mode" message sent assertEquals( 1, t.outbound.size(), "mode message sent"); assertEquals( "R 29 0 82", t.outbound.elementAt(0).toString(), "read message contents"); // send reply DCCppReply mr1 = DCCppReply.parseDCCppReply("r 0|82|29 12"); t.sendTestMessage(mr1); // At this point, the standard DCC++ programmer // should send a result to the programmer listeners, and // wait for either the next read/write request or for the // traffic controller to exit from service mode. We just // need to wait a few seconds and see that the listener we // registered earlier received the values we expected. JUnitUtil.waitFor(() -> !(l.getRcvdInvoked() == 0), "Rcvd not invoked"); assertNotEquals( 0, l.getRcvdInvoked(), "Receive Called by Programmer"); assertEquals( 12, l.getRcvdValue(), "Register mode received value"); } @Test public void testReadCvWithStartValSequence() throws JmriException { t.terminateThreads(); p.dispose(); l = null; t = null; p = null; // infrastructure objects DCCppCommandStation cs = new DCCppCommandStation(); cs.setVersion("3.0.0"); //set the version to support startVal assertEquals( "3.0.0", cs.getVersion()); t = new DCCppInterfaceScaffold(cs); l = new ProgListenerScaffold(); p = new DCCppProgrammer(t) { @Override protected synchronized void restartTimer(int delay) { super.restartTimer(RESTART_TIME); } }; // and do the read, with 12 as startVal p.readCV("29", l, 12); // check "prog mode" message sent assertEquals( 1, t.outbound.size(), "mode message sent"); assertEquals( "V 29 12", t.outbound.elementAt(0).toString(), "read message contents"); // send reply DCCppReply mr1 = DCCppReply.parseDCCppReply("v 29 12"); t.sendTestMessage(mr1); // At this point, the standard DCC++ programmer // should send a result to the programmer listeners, and // wait for either the next read/write request or for the // traffic controller to exit from service mode. We just // need to wait a few seconds and see that the listener we // registered earlier received the values we expected. JUnitUtil.waitFor(() -> !(l.getRcvdInvoked() == 0), "Rcvd not invoked"); //failure in this test occurs with the next line. assertNotEquals( 0, l.getRcvdInvoked()); assertEquals( 12, l.getRcvdValue(), "Register mode received value"); } @Test @Disabled("test is commented out") public void testReadRegisterSequence() throws JmriException { /* // infrastructure objects DCCppInterfaceScaffold t = new DCCppInterfaceScaffold(new DCCppCommandStation()); jmri.ProgListenerScaffold l = new jmri.ProgListenerScaffold(); DCCppProgrammer p = new DCCppProgrammer(t) { protected synchronized void restartTimer(int delay) { super.restartTimer(RESTART_TIME); } }; // set register mode p.setMode(ProgrammingMode.REGISTERMODE); // and do the read p.readCV("29", l); // check "prog mode" message sent Assert.assertEquals("mode message sent", 1, t.outbound.size()); Assert.assertEquals("read message contents", "22 11 05 36", t.outbound.elementAt(0).toString()); // send reply (enter service mode ) DCCppReply mr1 = new DCCppReply(); mr1.setElement(0, 0x61); mr1.setElement(1, 0x02); mr1.setElement(2, 0x63); t.sendTestMessage(mr1); Assert.assertEquals("inquire message sent", 2, t.outbound.size()); Assert.assertEquals("inquire message contents", "21 10 31", t.outbound.elementAt(1).toString()); // send a result string DCCppReply mr2 = new DCCppReply(); mr2.setElement(0, 0x63); mr2.setElement(1, 0x10); mr2.setElement(2, 0x05); mr2.setElement(3, 0x22); mr2.setElement(4, 0x54); t.sendTestMessage(mr2); // At this point, the standard XpressNet programmer // should send a result to the programmer listeners, and // wait for either the next read/write request or for the // traffic controller to exit from service mode. We just // need to wait a few seconds and see that the listener we // registered earlier received the values we expected. JUnitUtil.waitFor(() -> { return !(l.getRcvdInvoked() == 0); }); //failure in this test occurs with the next line. Assert.assertFalse("Receive Called by Programmer", l.getRcvdInvoked() == 0); Assert.assertEquals("Register mode received value", 34, l.getRcvdValue()); */ } // this test is the same as the testWriteCvSequence test, but // it checks the sequence for CVs greater than 256, which use // different XpressNet commands. @Test public void testWriteHighCvSequence() throws JmriException { // and do the write p.writeCV("300", 34, l); // check "prog mode" message sent assertEquals( 1, t.outbound.size(), "mode message sent"); assertEquals( "W 300 34 0 87", t.outbound.elementAt(0).toString(), "write message contents"); // send reply DCCppReply mr1 = DCCppReply.parseDCCppReply("r 0|87|300 34"); t.sendTestMessage(mr1); // At this point, the standard DCC++ programmer // should send a result to the programmer listeners, and // wait for either the next read/write request or for the // traffic controller to exit from service mode. We just // need to wait a few seconds and see that the listener we // registered earlier received the values we expected. JUnitUtil.waitFor(() -> !(l.getRcvdInvoked() == 0), "Rcvd not invoked"); assertNotEquals( 0, l.getRcvdInvoked(), "Receive Called by Programmer"); assertEquals( 34, l.getRcvdValue(), "Direct mode received value"); } // this test is the same as the testReadCvSequence test, but // it checks the sequence for CVs greater than 256, which use // different XpressNet commands. @Test public void testReadCvHighSequence() throws JmriException { // and do the read p.readCV("300", l); // check "prog mode" message sent assertEquals( 1, t.outbound.size(), "mode message sent"); assertEquals( "R 300 0 82", t.outbound.elementAt(0).toString(), "read message contents"); // send reply DCCppReply mr1 = DCCppReply.parseDCCppReply("r 0|82|300 34"); t.sendTestMessage(mr1); // At this point, the standard DCC++ programmer // should send a result to the programmer listeners, and // wait for either the next read/write request or for the // traffic controller to exit from service mode. We just // need to wait a few seconds and see that the listener we // registered earlier received the values we expected. JUnitUtil.waitFor(() -> !(l.getRcvdInvoked() == 0), "Rcvd not invoked"); assertNotEquals( 0, l.getRcvdInvoked(), "Receive Called by Programmer"); assertEquals( 34, l.getRcvdValue(), "Direct mode received value"); } // Test to make sure the getCanWrite(int,string) function works correctly // TODO: Fix test to verify exception thrown for Register and paged modes. @Test @Override public void testGetCanWrite() { //p.setMode(ProgrammingMode.REGISTERMODE); //Assert.assertTrue("DCC++ Base Station can write CV3 in Register Mode", p.getCanWrite("3")); //p.setMode(ProgrammingMode.PAGEMODE); //Assert.assertTrue("DCC++ Base Station Can Write CV3 in paged mode", p.getCanWrite("3")); p.setMode(ProgrammingMode.DIRECTBYTEMODE); assertTrue( p.getCanWrite("3"), "DCC++ Base Station Can Write CV3 in direct byte mode"); // p.setMode(ProgrammingMode.DIRECTBITMODE); // Assert.assertFalse("DCC++ Base Station Can Not Write CV3 in direct bit mode", p.getCanWrite("3")); //p.setMode(ProgrammingMode.REGISTERMODE); //Assert.assertFalse("DCC++ Base Station Can not Write CV300 in register mode", p.getCanWrite("300")); //p.setMode(ProgrammingMode.PAGEMODE); //Assert.assertFalse("DCC++ Base Station Can not Write CV300 in paged mode", p.getCanWrite("300")); p.setMode(ProgrammingMode.DIRECTBYTEMODE); assertTrue( p.getCanWrite("300"), "DCC++ Base Station Can Write CV300 in direct byte mode"); // p.setMode(ProgrammingMode.DIRECTBITMODE); // Assert.assertFalse("DCC++ Base Station Can Not Write CV300 in direct bit mode", p.getCanWrite("300")); p.setMode(ProgrammingMode.DIRECTBYTEMODE); assertFalse( p.getCanWrite("3000"), "DCC++ Base Station Can Not Write CV3000 in direct byte mode"); // p.setMode(ProgrammingMode.DIRECTBITMODE); // Assert.assertFalse("DCC++ Base Station Can Not Write CV3000 in direct bit mode", p.getCanWrite("3000")); } // Test to make sure the getCanRead(int,string) function works correctly // TODO: Fix test to verify exception thrown for Register and paged modes. @Test @Override public void testGetCanRead() { //p.setMode(ProgrammingMode.REGISTERMODE); //Assert.assertTrue("DCC++ Base Station Can Read CV3 in register mode", p.getCanRead("3")); //p.setMode(ProgrammingMode.PAGEMODE); //Assert.assertTrue("DCC++ Base Station Can Read CV3 in paged mode", p.getCanRead("3")); p.setMode(ProgrammingMode.DIRECTBYTEMODE); assertTrue( p.getCanRead("3"), "DCC++ Base Station Can Read CV3 in direct byte mode"); // p.setMode(ProgrammingMode.DIRECTBITMODE); // Assert.assertFalse("DCC++ Base Station Can Not Read CV3 in direct bit mode", p.getCanRead("3")); //p.setMode(ProgrammingMode.REGISTERMODE); //Assert.assertFalse("DCC++ Base Station Can not Read CV300 in register mode", p.getCanRead("300")); //p.setMode(ProgrammingMode.PAGEMODE); //Assert.assertFalse("DCC++ Base Station Can not Read CV300 in paged mode", p.getCanRead("300")); p.setMode(ProgrammingMode.DIRECTBYTEMODE); assertTrue( p.getCanRead("300"), "DCC++ Base Station Can Read CV300 in direct byte mode"); // p.setMode(ProgrammingMode.DIRECTBITMODE); // Assert.assertFalse("DCC++ Base Station Can Not Read CV300 in direct bit mode", p.getCanRead("300")); p.setMode(ProgrammingMode.DIRECTBYTEMODE); assertFalse( p.getCanRead("3000"), "DCC++ Base Station Can not Read CV3000 in direct byte mode"); // p.setMode(ProgrammingMode.DIRECTBITMODE); // Assert.assertFalse("DCC++ Base Station Can not Read CV3000 in direct bit mode", p.getCanRead("3000")); } @Override @BeforeEach public void setUp() { JUnitUtil.setUp(); // infrastructure objects t = new DCCppInterfaceScaffold(new DCCppCommandStation()); l = new ProgListenerScaffold(); p = new DCCppProgrammer(t) { @Override protected synchronized void restartTimer(int delay) { super.restartTimer(RESTART_TIME); } }; programmer = p; } @Override @AfterEach public void tearDown() { p.dispose(); t.terminateThreads(); t = null; l = null; p = null; JUnitUtil.tearDown(); } }