Skip to content

Commit 18b3999

Browse files
committed
Add control line tests (DTR/DSR and RTS/CTS).
1 parent 2237113 commit 18b3999

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package gnu.io;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.TooManyListenersException;
7+
import java.util.concurrent.CountDownLatch;
8+
import java.util.concurrent.TimeUnit;
9+
import java.util.logging.Logger;
10+
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.extension.RegisterExtension;
13+
14+
/**
15+
* Test the ability of the {@link SerialPort} implementation to manage the
16+
* control lines of the port (DTR/DSR and RTS/CTS).
17+
* <p>
18+
* This test is nonspecific as to <em>which</em> implementation; it exercises
19+
* only the public interface of the Java Communications API. Ports are opened
20+
* by the {@link SerialPortExtension} test extension, presumably by way of
21+
* {@link CommPortIdentifier}.
22+
*/
23+
public class SerialPortControlTest
24+
{
25+
private static final Logger log = Logger.getLogger(SerialPortControlTest.class.getName());
26+
27+
/**
28+
* How long to wait (in milliseconds) for asynchronous events to arrive
29+
* before failing the test.
30+
*/
31+
private static final long EVENT_TIMEOUT = 250;
32+
33+
@RegisterExtension
34+
SerialPortExtension ports = new SerialPortExtension();
35+
36+
/**
37+
* Test that toggling DTR on a port changes the DSR state of another.
38+
*/
39+
@Test
40+
void testDTRDSRPolling()
41+
{
42+
this.ports.a.setDTR(false);
43+
assertFalse(this.ports.a.isDTR());
44+
assertFalse(this.ports.b.isDSR());
45+
46+
this.ports.a.setDTR(true);
47+
assertTrue(this.ports.a.isDTR());
48+
assertTrue(this.ports.b.isDSR());
49+
50+
this.ports.a.setDTR(false);
51+
assertFalse(this.ports.a.isDTR());
52+
assertFalse(this.ports.b.isDSR());
53+
}
54+
55+
/**
56+
* Test that toggling RTS on a port changes the CTS state of another.
57+
*/
58+
@Test
59+
void testRTSCTSPolling()
60+
{
61+
this.ports.a.setRTS(false);
62+
assertFalse(this.ports.a.isRTS());
63+
assertFalse(this.ports.b.isCTS());
64+
65+
this.ports.a.setRTS(true);
66+
assertTrue(this.ports.a.isRTS());
67+
assertTrue(this.ports.b.isCTS());
68+
69+
this.ports.a.setRTS(false);
70+
assertFalse(this.ports.a.isRTS());
71+
assertFalse(this.ports.b.isCTS());
72+
}
73+
74+
/* Use of CountDownLatch in the asynchronous event tests is based on
75+
* https://stackoverflow.com/a/1829949/640170. */
76+
77+
/**
78+
* Test that toggling DTR on a port changes generates a DSR event on
79+
* another.
80+
*
81+
* @throws TooManyListenersException if the port has not been properly
82+
* cleaned up after previous tests
83+
* @throws InterruptedException if the test is interrupted while waiting
84+
* for the event
85+
*/
86+
@Test
87+
void testDTRDSREvents() throws TooManyListenersException, InterruptedException
88+
{
89+
CountDownLatch latch = new CountDownLatch(1);
90+
this.ports.b.addEventListener(ev -> {
91+
if (ev.getEventType() == SerialPortEvent.DSR)
92+
{
93+
latch.countDown();
94+
}
95+
});
96+
this.ports.b.notifyOnDSR(true);
97+
this.ports.a.setDTR(true);
98+
boolean sawEvent = false;
99+
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
100+
if (!sawEvent)
101+
{
102+
/* FIXME: The hard part about adding tests is that sometimes you
103+
* find bugs. The DSR event _is_ generated: set a breakpoint in the
104+
* event callback, or a print statement, and it will reliably fire
105+
* – but only _after_ this await. The callback _is_ run from the
106+
* monitor thread, as you'd expect it to be, and without the await,
107+
* the listener is called within milliseconds of DTR being
108+
* asserted; but block the main thread (with this await, or with
109+
* `Thread.sleep()`), and the event will never happen.
110+
*
111+
* I'm mystified by this behaviour, because the callback
112+
* configuration here is the same as in `testRTSCTSEvents()`, where
113+
* it works as expected.
114+
*
115+
* One thing which does seem to cause the event to propagate is
116+
* de-asserting DTR. Then _that_ event gets lost, of course, but it
117+
* seems to prompt the first event to make its way through. So
118+
* that's the workaround employed here for now; but this really is
119+
* papering over a hole. The DSR event being one edge late means
120+
* it's basically impossible for any consumer to hand-roll their
121+
* own hardware flow control. */
122+
log.warning("DSR event propagation is buggy! DSR events are useless until this is fixed.");
123+
this.ports.a.setDTR(false);
124+
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
125+
}
126+
assertTrue(sawEvent);
127+
}
128+
129+
/**
130+
* Test that toggling RTS on a port changes generates a CTS event on
131+
* another.
132+
*
133+
* @throws TooManyListenersException if the port has not been properly
134+
* cleaned up after previous tests
135+
* @throws InterruptedException if the test is interrupted while waiting
136+
* for the event
137+
*/
138+
@Test
139+
void testRTSCTSEvents() throws TooManyListenersException, InterruptedException
140+
{
141+
CountDownLatch latch = new CountDownLatch(1);
142+
this.ports.b.addEventListener(ev -> {
143+
if (ev.getEventType() == SerialPortEvent.CTS)
144+
{
145+
latch.countDown();
146+
}
147+
});
148+
this.ports.b.notifyOnCTS(true);
149+
this.ports.a.setRTS(true);
150+
assertTrue(latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS));
151+
}
152+
}

0 commit comments

Comments
 (0)