Skip to content

Commit 13876df

Browse files
committed
Add control line tests (DTR/DSR and RTS/CTS).
1 parent 7c316dd commit 13876df

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
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+
private static final String BUGGY_DSR_EVENTS = "DSR event propagation is buggy! DSR events are useless until this is fixed.";
28+
private static final String BUGGY_CTS_EVENTS = "CTS event propagation is buggy! CTS events are useless until this is fixed.";
29+
30+
/**
31+
* How long to wait (in milliseconds) for asynchronous events to arrive
32+
* before failing the test.
33+
*/
34+
private static final long EVENT_TIMEOUT = 250;
35+
36+
@RegisterExtension
37+
SerialPortExtension ports = new SerialPortExtension();
38+
39+
/**
40+
* Test that toggling DTR on a port changes the DSR state of another.
41+
*/
42+
@Test
43+
void testDTRDSRPolling()
44+
{
45+
this.ports.a.setDTR(false);
46+
assertFalse(this.ports.a.isDTR());
47+
assertFalse(this.ports.b.isDSR());
48+
49+
this.ports.a.setDTR(true);
50+
assertTrue(this.ports.a.isDTR());
51+
assertTrue(this.ports.b.isDSR());
52+
53+
this.ports.a.setDTR(false);
54+
assertFalse(this.ports.a.isDTR());
55+
assertFalse(this.ports.b.isDSR());
56+
}
57+
58+
/**
59+
* Test that toggling RTS on a port changes the CTS state of another.
60+
*/
61+
@Test
62+
void testRTSCTSPolling()
63+
{
64+
this.ports.a.setRTS(false);
65+
assertFalse(this.ports.a.isRTS());
66+
assertFalse(this.ports.b.isCTS());
67+
68+
this.ports.a.setRTS(true);
69+
assertTrue(this.ports.a.isRTS());
70+
assertTrue(this.ports.b.isCTS());
71+
72+
this.ports.a.setRTS(false);
73+
assertFalse(this.ports.a.isRTS());
74+
assertFalse(this.ports.b.isCTS());
75+
}
76+
77+
/* Use of CountDownLatch in the asynchronous event tests is based on
78+
* https://stackoverflow.com/a/1829949/640170. */
79+
80+
/**
81+
* Test that toggling DTR on a port changes generates a DSR event on
82+
* another.
83+
*
84+
* @throws TooManyListenersException if the port has not been properly
85+
* cleaned up after previous tests
86+
* @throws InterruptedException if the test is interrupted while waiting
87+
* for the event
88+
*/
89+
@Test
90+
void testDTRDSREvents() throws TooManyListenersException, InterruptedException
91+
{
92+
CountDownLatch latch = new CountDownLatch(1);
93+
this.ports.b.addEventListener(ev -> {
94+
if (ev.getEventType() == SerialPortEvent.DSR)
95+
{
96+
latch.countDown();
97+
}
98+
});
99+
this.ports.b.notifyOnDSR(true);
100+
this.ports.a.setDTR(true);
101+
boolean sawEvent = false;
102+
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
103+
if (!sawEvent)
104+
{
105+
/* FIXME: The hard part about adding tests is that sometimes you
106+
* find bugs. The DSR event _is_ generated: set a breakpoint in the
107+
* event callback, or a print statement, and it will reliably fire
108+
* – but only _after_ this await. The callback _is_ run from the
109+
* monitor thread, as you'd expect it to be, and without the await,
110+
* the listener is called within milliseconds of DTR being
111+
* asserted; but block the main thread (with this await, or with
112+
* `Thread.sleep()`), and the event will never happen.
113+
*
114+
* I'm mystified by this behaviour, because the callback
115+
* configuration here is the same as in `testRTSCTSEvents()`, where
116+
* it works as expected.
117+
*
118+
* One thing which does seem to cause the event to propagate is
119+
* de-asserting DTR. Then _that_ event gets lost, of course, but it
120+
* seems to prompt the first event to make its way through. So
121+
* that's the workaround employed here for now; but this really is
122+
* papering over a hole. The DSR event being one edge late means
123+
* it's basically impossible for any consumer to hand-roll their
124+
* own hardware flow control.
125+
*
126+
* This is broken on Windows (10), macOS (10.15), and Linux
127+
* (4.19.0). */
128+
log.warning(SerialPortControlTest.BUGGY_DSR_EVENTS);
129+
this.ports.a.setDTR(false);
130+
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
131+
}
132+
assertTrue(sawEvent);
133+
}
134+
135+
/**
136+
* Test that toggling RTS on a port changes generates a CTS event on
137+
* another.
138+
*
139+
* @throws TooManyListenersException if the port has not been properly
140+
* cleaned up after previous tests
141+
* @throws InterruptedException if the test is interrupted while waiting
142+
* for the event
143+
*/
144+
@Test
145+
void testRTSCTSEvents() throws TooManyListenersException, InterruptedException
146+
{
147+
CountDownLatch latch = new CountDownLatch(1);
148+
this.ports.b.addEventListener(ev -> {
149+
if (ev.getEventType() == SerialPortEvent.CTS)
150+
{
151+
latch.countDown();
152+
}
153+
});
154+
this.ports.b.notifyOnCTS(true);
155+
this.ports.a.setRTS(true);
156+
boolean sawEvent = false;
157+
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
158+
if (!sawEvent)
159+
{
160+
/* FIXME: Same story here as with DSR events. This works correctly
161+
* on Windows (10), but fails on macOS (10.15) and Linux
162+
* (4.19.0). */
163+
log.warning(SerialPortControlTest.BUGGY_CTS_EVENTS);
164+
this.ports.a.setRTS(false);
165+
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
166+
}
167+
assertTrue(sawEvent);
168+
}
169+
}

0 commit comments

Comments
 (0)