de.ibapl.spsw.mock.MockSerialPortSocket Maven / Gradle / Ivy
The newest version!
/*
* SPSW - Drivers for the serial port, https://github.com/aploese/spsw/
* Copyright (C) 2009-2021, Arne Plöse and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package de.ibapl.spsw.mock;
import de.ibapl.spsw.api.AsyncSerialPortSocket;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import de.ibapl.spsw.api.DataBits;
import de.ibapl.spsw.api.FlowControl;
import de.ibapl.spsw.api.Parity;
import de.ibapl.spsw.api.SerialPortSocket;
import de.ibapl.spsw.api.Speed;
import de.ibapl.spsw.api.StopBits;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractInterruptibleChannel;
import java.util.concurrent.Future;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* Helper class for test that interact with a SerialPortSocket. The behavior of
* a SerialPortSocket can be predefined. Example:
*
*
* {@code
* mockSerialPortSocket = new MockSerialPortSocket();
* mockSerialPortSocket.expectedWrite("0102");
* mockSerialPortSocket.expectedRead("0201");
* mockSerialPortSocket.expectedWrite("0304");
* mockSerialPortSocket.expectedRead("0403");
*
* SerialPortSocket serialPortSocket = mockSerialPortSocket;
*
* serialPortSocket.open();
* serialPortSocket.getOutputStream().write(0x01);
* serialPortSocket.getOutputStream().write(0x02);
* assertEquals(0x02, serialPortSocket.getInputStream().read());
* assertEquals(0x01, serialPortSocket.getInputStream().read());
* assertThrows(UnexpectedRequestError.class, () -> {
* serialPortSocket.getInputStream().read(); });
*
* }
*
*
*
* @author Arne Plöse
*/
public class MockSerialPortSocket extends AbstractInterruptibleChannel implements SerialPortSocket, AsyncSerialPortSocket {
@Override
public int read(ByteBuffer dst) throws IOException {
//TODO
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public int write(ByteBuffer src) throws IOException {
//TODO
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected void implCloseChannel() throws IOException {
//no-op
}
@Override
public void readAsync(ByteBuffer dst, Consumer callbackOnSuccess, Consumer callbackOnFailure) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void readAsync(ByteBuffer dst, BiConsumer callback) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public Future readAsync(ByteBuffer dst) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeAsync(ByteBuffer src, Consumer callbackOnSuccess, Consumer callbackOnFailure) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void writeAsync(ByteBuffer src, BiConsumer callback) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public Future writeAsync(ByteBuffer src) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void drainOutputBuffer() throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
public class MockInputStream extends InputStream {
int readPtr = 0;
@Override
public int available() throws IOException {
ensureOpen();
if (factory.isDataEmpty()) {
return 0;
}
final MockRequest> firstRequest = factory.getDataFirst();
if (firstRequest.requestType != MockRequestType.READ) {
return 0;
}
if (firstRequest instanceof MockExceptionRequest) {
return 0;
}
if (firstRequest instanceof MockDataRequest) {
final MockDataRequest dataRequest = (MockDataRequest) firstRequest;
return dataRequest.payload.length - readPtr;
} else {
throw new UnexpectedRequestError("No read data request", firstRequest.stackException);
}
}
@Override
public void close() throws IOException {
MockSerialPortSocket.this.close();
}
@Override
public int read() throws IOException {
ensureOpen();
if (factory.isDataEmpty()) {
throw new UnexpectedRequestError("data is empty");
}
final MockRequest> firstRequest = factory.getDataFirst();
if (firstRequest.requestType != MockRequestType.READ) {
throw new UnexpectedRequestError("No Read request", firstRequest.stackException);
}
if (firstRequest instanceof MockExceptionRequest) {
final MockExceptionRequest exceptionRequest = (MockExceptionRequest) firstRequest;
factory.removeDataFirst();
throw exceptionRequest.payload;
}
if (firstRequest instanceof MockDataRequest) {
final MockDataRequest dataRequest = (MockDataRequest) firstRequest;
int result = dataRequest.payload[readPtr++];
if (readPtr == dataRequest.payload.length) {
readPtr = 0;
factory.removeDataFirst();
}
return result;
} else {
throw new UnexpectedRequestError("No read data request", firstRequest.stackException);
}
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
ensureOpen();
if (factory.isDataEmpty()) {
throw new UnexpectedRequestError("data is empty");
}
final MockRequest> firstRequest = factory.getDataFirst();
if (firstRequest.requestType != MockRequestType.READ) {
throw new UnexpectedRequestError("No Read request", firstRequest.stackException);
}
if (firstRequest instanceof MockExceptionRequest) {
final MockExceptionRequest exceptionRequest = (MockExceptionRequest) firstRequest;
factory.removeDataFirst();
throw exceptionRequest.payload;
}
if (firstRequest instanceof MockDataRequest) {
final MockDataRequest dataRequest = (MockDataRequest) firstRequest;
int count = dataRequest.payload.length - readPtr;
if (len < count) {
count = len;
}
System.arraycopy(dataRequest.payload, readPtr, b, off, count);
readPtr += count;
if (readPtr == dataRequest.payload.length) {
readPtr = 0;
factory.removeDataFirst();
}
return count;
} else {
throw new UnexpectedRequestError("No read data request", firstRequest.stackException);
}
}
}
public class MockOutputStream extends OutputStream {
private int writePtr = 0;
@Override
public void close() throws IOException {
MockSerialPortSocket.this.close();
}
@Override
public void write(int b) throws IOException {
ensureOpen();
if (factory.isDataEmpty()) {
throw new UnexpectedRequestError("data is empty");
}
final MockRequest> firstRequest = factory.getDataFirst();
if (firstRequest.requestType != MockRequestType.WRITE) {
throw new UnexpectedRequestError("No Write request", firstRequest.stackException);
}
if (firstRequest instanceof MockExceptionRequest) {
final MockExceptionRequest exceptionRequest = (MockExceptionRequest) firstRequest;
factory.removeDataFirst();
throw exceptionRequest.payload;
}
if (firstRequest instanceof MockDataRequest) {
final MockDataRequest dataRequest = (MockDataRequest) firstRequest;
if (b != dataRequest.payload[writePtr++]) {
throw new UnexpectedRequestError("Not expected write data at " + (writePtr - 1),
firstRequest.stackException);
}
if (writePtr == dataRequest.payload.length) {
writePtr = 0;
factory.removeDataFirst();
}
} else {
throw new UnexpectedRequestError("No read data request", firstRequest.stackException);
}
}
}
public static class UnexpectedRequestError extends Error {
/**
*
*/
private static final long serialVersionUID = 1L;
public UnexpectedRequestError(String message) {
super(message);
}
public UnexpectedRequestError(String message, MockRequestStackException stackException) {
super(message, stackException);
}
}
public static byte[] ascii2Bytes(String s) {
byte[] result = new byte[s.length() / 2];
for (int i = 0; i < (s.length() / 2); i++) {
result[i] = (byte) Integer.parseInt(s.substring(i * 2, (i * 2) + 2), 16);
}
return result;
}
public static String bytes2Ascii(byte[] byteArray) {
StringBuilder sb = new StringBuilder(byteArray.length);
for (byte b : byteArray) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private ExecutorService executor;
private Speed speed;
private DataBits dataBits;
private Set flowControl;
private int interByteReadTimeout;
private MockInputStream is;
private MockOutputStream os;
private final MockSerialPortFactory factory;
private final String portname;
private int overallReadTimeout;
private int overallWriteTimeout;
private Parity parity;
private StopBits stopBits;
@Override
public Speed getSpeed() throws IOException {
ensureOpen();
return speed;
}
@Override
public DataBits getDatatBits() throws IOException {
ensureOpen();
return dataBits;
}
@Override
public Set getFlowControl() throws IOException {
ensureOpen();
return flowControl;
}
@Override
public int getInBufferBytesCount() throws IOException {
ensureOpen();
return is.available();
}
@Override
public InputStream getInputStream() throws IOException {
ensureOpen();
return is;
}
@Override
public int getInterByteReadTimeout() throws IOException {
ensureOpen();
return interByteReadTimeout;
}
@Override
public int getOutBufferBytesCount() throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public OutputStream getOutputStream() throws IOException {
ensureOpen();
return os;
}
@Override
public int getOverallReadTimeout() throws IOException {
ensureOpen();
return overallReadTimeout;
}
@Override
public int getOverallWriteTimeout() throws IOException {
ensureOpen();
return overallWriteTimeout;
}
@Override
public Parity getParity() throws IOException {
ensureOpen();
return parity;
}
@Override
public String getPortName() {
return this.getClass().getCanonicalName();
}
@Override
public StopBits getStopBits() throws IOException {
ensureOpen();
return stopBits;
}
@Override
public char getXOFFChar() throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public char getXONChar() throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public boolean isCTS() throws IOException {
ensureOpen();
return false;
}
@Override
public boolean isDCD() throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public boolean isDSR() throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
protected void ensureOpen() throws IOException {
if (!isOpen()) {
throw new IOException(PORT_IS_CLOSED);
}
}
@Override
public boolean isRI() throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
public MockSerialPortSocket(MockSerialPortFactory factory, String portname) throws IOException {
is = new MockInputStream();
os = new MockOutputStream();
this.factory = factory;
this.portname = portname;
}
public MockSerialPortSocket(MockSerialPortFactory factory, String portname, Speed speed, DataBits dataBits, StopBits stopBits, Parity parity, Set flowControls)
throws IOException {
this(factory, portname);
this.speed = speed;
this.dataBits = dataBits;
this.stopBits = stopBits;
this.parity = parity;
this.flowControl = flowControls;
}
public MockSerialPortSocket(MockSerialPortFactory factory, String portname, ExecutorService executor) throws IOException {
this(factory, portname);
this.executor = executor;
}
@Override
public void sendBreak(int duration) throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public void sendXOFF() throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public void sendXON() throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public void setSpeed(Speed speed) throws IOException {
ensureOpen();
this.speed = speed;
}
@Override
public void setBreak(boolean value) throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public void setDataBits(DataBits dataBits) throws IOException {
ensureOpen();
this.dataBits = dataBits;
}
@Override
public void setDTR(boolean value) throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public void setFlowControl(Set flowControls) throws IOException {
ensureOpen();
this.flowControl = flowControls;
}
@Override
public void setParity(Parity parity) throws IOException {
ensureOpen();
this.parity = parity;
}
@Override
public void setRTS(boolean value) throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public void setStopBits(StopBits stopBits) throws IOException {
ensureOpen();
this.stopBits = stopBits;
}
@Override
public void setTimeouts(int interByteReadTimeout, int overallReadTimeout, int overallWriteTimeout)
throws IOException {
ensureOpen();
this.interByteReadTimeout = interByteReadTimeout;
this.overallReadTimeout = overallReadTimeout;
this.overallWriteTimeout = overallWriteTimeout;
}
@Override
public void setXOFFChar(char c) throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
@Override
public void setXONChar(char c) throws IOException {
ensureOpen();
throw new RuntimeException("Not Implemented");
}
}