Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functional tests #182

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ archivesBaseName = "nrjavaserial"
version = props."app.version"

sourceSets {
test {
java {
srcDirs = ["test/src"]
}
}
main {
resources {
srcDirs = ["src/main/resources", "src/main/c/resources"]
Expand All @@ -40,7 +35,7 @@ repositories {

dependencies {
compile fileTree(dir: 'libs', includes: ['*.jar'])
testCompile 'junit:junit:4.12'
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
compile 'commons-net:commons-net:3.3'
compileOnly 'net.java.dev.jna:jna:4.4.0'
compileOnly 'net.java.dev.jna:jna-platform:4.4.0'
Expand All @@ -62,6 +57,13 @@ jar {

}

test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}

task javadocJar(type: Jar) {
classifier = 'javadoc'
from javadoc
Expand Down
26 changes: 26 additions & 0 deletions src/test/java/gnu/io/CommPortIdentifierTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package gnu.io;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.util.Enumeration;

import org.junit.jupiter.api.Test;

class CommPortIdentifierTest
{
@Test
void testGetCommPortIdentifiers()
{
@SuppressWarnings("rawtypes")
Enumeration identifiers = CommPortIdentifier.getPortIdentifiers();
assertNotNull(identifiers);

/* Hard to assert on anything else when the test hardware may change
* dramatically.
*
* TODO: Wire up SerialPortExtension without its test-disabling
* ExecutionCondition behaviour, and use the names of its configured
* ports to confirm that the identifiers enumeration at least contains
* something when we know what to expect in it. */
}
}
169 changes: 169 additions & 0 deletions src/test/java/gnu/io/SerialPortControlTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package gnu.io;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.TooManyListenersException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

/**
* Test the ability of the {@link SerialPort} implementation to manage the
* control lines of the port (DTR/DSR and RTS/CTS).
* <p>
* This test is nonspecific as to <em>which</em> implementation; it exercises
* only the public interface of the Java Communications API. Ports are opened
* by the {@link SerialPortExtension} test extension, presumably by way of
* {@link CommPortIdentifier}.
*/
public class SerialPortControlTest
{
private static final Logger log = Logger.getLogger(SerialPortControlTest.class.getName());

private static final String BUGGY_DSR_EVENTS = "DSR event propagation is buggy! DSR events are useless until this is fixed.";
private static final String BUGGY_CTS_EVENTS = "CTS event propagation is buggy! CTS events are useless until this is fixed.";

/**
* How long to wait (in milliseconds) for asynchronous events to arrive
* before failing the test.
*/
private static final long EVENT_TIMEOUT = 250;

@RegisterExtension
SerialPortExtension ports = new SerialPortExtension();

/**
* Test that toggling DTR on a port changes the DSR state of another.
*/
@Test
void testDTRDSRPolling()
{
this.ports.a.setDTR(false);
assertFalse(this.ports.a.isDTR());
assertFalse(this.ports.b.isDSR());

this.ports.a.setDTR(true);
assertTrue(this.ports.a.isDTR());
assertTrue(this.ports.b.isDSR());

this.ports.a.setDTR(false);
assertFalse(this.ports.a.isDTR());
assertFalse(this.ports.b.isDSR());
}

/**
* Test that toggling RTS on a port changes the CTS state of another.
*/
@Test
void testRTSCTSPolling()
{
this.ports.a.setRTS(false);
assertFalse(this.ports.a.isRTS());
assertFalse(this.ports.b.isCTS());

this.ports.a.setRTS(true);
assertTrue(this.ports.a.isRTS());
assertTrue(this.ports.b.isCTS());

this.ports.a.setRTS(false);
assertFalse(this.ports.a.isRTS());
assertFalse(this.ports.b.isCTS());
}

/* Use of CountDownLatch in the asynchronous event tests is based on
* https://stackoverflow.com/a/1829949/640170. */

/**
* Test that toggling DTR on a port changes generates a DSR event on
* another.
*
* @throws TooManyListenersException if the port has not been properly
* cleaned up after previous tests
* @throws InterruptedException if the test is interrupted while waiting
* for the event
*/
@Test
void testDTRDSREvents() throws TooManyListenersException, InterruptedException
{
CountDownLatch latch = new CountDownLatch(1);
this.ports.b.addEventListener(ev -> {
if (ev.getEventType() == SerialPortEvent.DSR)
{
latch.countDown();
}
});
this.ports.b.notifyOnDSR(true);
this.ports.a.setDTR(true);
boolean sawEvent = false;
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
if (!sawEvent)
{
/* FIXME: The hard part about adding tests is that sometimes you
* find bugs. The DSR event _is_ generated: set a breakpoint in the
* event callback, or a print statement, and it will reliably fire
* – but only _after_ this await. The callback _is_ run from the
* monitor thread, as you'd expect it to be, and without the await,
* the listener is called within milliseconds of DTR being
* asserted; but block the main thread (with this await, or with
* `Thread.sleep()`), and the event will never happen.
*
* I'm mystified by this behaviour, because the callback
* configuration here is the same as in `testRTSCTSEvents()`, where
* it works as expected.
*
* One thing which does seem to cause the event to propagate is
* de-asserting DTR. Then _that_ event gets lost, of course, but it
* seems to prompt the first event to make its way through. So
* that's the workaround employed here for now; but this really is
* papering over a hole. The DSR event being one edge late means
* it's basically impossible for any consumer to hand-roll their
* own hardware flow control.
*
* This is broken on Windows (10), macOS (10.15), and Linux
* (4.19.0). */
log.warning(SerialPortControlTest.BUGGY_DSR_EVENTS);
this.ports.a.setDTR(false);
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
}
assertTrue(sawEvent);
}

/**
* Test that toggling RTS on a port changes generates a CTS event on
* another.
*
* @throws TooManyListenersException if the port has not been properly
* cleaned up after previous tests
* @throws InterruptedException if the test is interrupted while waiting
* for the event
*/
@Test
void testRTSCTSEvents() throws TooManyListenersException, InterruptedException
{
CountDownLatch latch = new CountDownLatch(1);
this.ports.b.addEventListener(ev -> {
if (ev.getEventType() == SerialPortEvent.CTS)
{
latch.countDown();
}
});
this.ports.b.notifyOnCTS(true);
this.ports.a.setRTS(true);
boolean sawEvent = false;
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
if (!sawEvent)
{
/* FIXME: Same story here as with DSR events. This works correctly
* on Windows (10), but fails on macOS (10.15) and Linux
* (4.19.0). */
log.warning(SerialPortControlTest.BUGGY_CTS_EVENTS);
this.ports.a.setRTS(false);
sawEvent = latch.await(SerialPortControlTest.EVENT_TIMEOUT, TimeUnit.MILLISECONDS);
}
assertTrue(sawEvent);
}
}
Loading