Search code examples
javamultithreadingsocketstestinginputstream

Java - Testing socket communication with jUnit


I'm testing a class that handles communications based on a socket using jUnit 4. My test class launches a thread that simulates the client.

private class BeaconSimulator implements Runnable {

    private String address = null;
    private int port = 0;

    BeaconSimulator(String address, int port) {
        this.address = address;
        this.port = port;
    }

    @Override
    public void run() {
        try (
           Socket s = new Socket(address, port); 
           InputStream is = s.getInputStream();
 
           OutputStream os = s.getOutputStream()) {

           IOUtils.write(DatatypeConverter.parseHexBinary(
           "02000A00080001113E419F00D8000AB0ACB9AC309C22D84A11"), os);
           
           ack = IOUtils.toByteArray(is);

        } catch (UnknownHostException e) {
        
            System.err.print(e);
        
        } catch (IOException e) {
            
           System.err.print(e);
        }
    }

}

I launch it this way :

@Test
public void testBeaconCommunicationHandlerProcess() throws CustomException, InterruptedException, IOException {
    CustomBean bean = new CustomBean();
    ServerSocket server = new ServerSocket(8088);
    Thread t = new Thread(new BeaconSimulator("localhost", 8088));

    t.start();
    bean.setSocket(server.accept());
    new BeaconCommunicationHandler(bean).execute();
    t.join();
    assertArrayEquals(DatatypeConverter.parseHexBinary("0500000000"), ack);
    server.close();
}

The execute method of the BeaconCommunicationHandler object does the following :

LOG.info("Communication with {} started", getRunnableBean().getSocket().getInetAddress());
try (
   InputStream is = getRunnableBean().getSocket().getInputStream();
   OutputStream os = getRunnableBean().getSocket().getOutputStream()) {
    LOG.info("Reading MO on socket {}", getRunnableBean().getSocket().getInetAddress());
    try {
        message = IOUtils.toByteArray(is);
    } catch (IOException e) {
        throw new FunctionalGenException("Failed to read on socket", e);
    }
}
LOG.debug("MO from {} -> {}", getRunnableBean().getSocket().getInetAddress(), Hex.encodeHexString(message).toUpperCase());

LOG.info("Ending communication with {}", getRunnableBean().getSocket().getInetAddress());
try (DataOutputStream dos = new DataOutputStream(os)) {
    dos.write(DatatypeConverter.parseHexBinary("0500000000"));
} catch (IOException e) {
    throw new FunctionalGenException("Failed to send the final packet", e);
}

The problem is that when I don't try to read in my BeaconSimulator thread (by removing the line ack = IOUtils.toByteArray(is)), everything runs to the end, but if I try to read, the test blocks.

Without the line ack = IOUtils.toByteArray(is) :

02-07-2020 14:23:57 INFO     - main - BeaconCommunicationHandler     - Communication with /127.0.0.1 started
02-07-2020 14:23:57 INFO     - main - BeaconCommunicationHandler     - Reading MO on socket /127.0.0.1
02-07-2020 14:23:57 DEBUG    - main - BeaconCommunicationHandler     - MO from /127.0.0.1 -> 02000A00080001113E419F00D8000AB0ACB9AC309C22D84A11
02-07-2020 14:23:57 INFO     - main - BeaconCommunicationHandler     - Ending communication with /127.0.0.1
02-07-2020 14:23:57 INFO     - main - BeaconCommunicationHandler     - Communication with /127.0.0.1 ended

With the line ack = IOUtils.toByteArray(is) :

02-07-2020 13:51:07 INFO     - main - BeaconCommunicationHandler     - Communication with /127.0.0.1 started
02-07-2020 13:51:07 INFO     - main - BeaconCommunicationHandler     - Reading MO on socket /127.0.0.1

And it's stuck there.

Thank you for your help


Solution

  • Thanks to Damian229 I did some tinkering and found a solution. It seems the IOUtils methods are not working properly. I replaced the writing methods by the "native" OutputStream.write(byte[]) method that is simple to use, and replaced the read method by the following :

    public byte[] readInputStream(InputStream is) throws IOException {
        byte[] message = null;
        byte[] buf = new byte[READ_SIZE];
        int readSize = -1;
    
        do {
            readSize = is.read(buf);
            if (readSize != -1) {
                buf = Arrays.copyOfRange(buf, 0, readSize);
            }
            message = message == null ? buf.clone() : ArrayUtils.addAll(message, buf);
        } while (readSize == READ_SIZE);
        return message;
    }
    

    I have made this method so it is used just as the IOUtils.read(InputStream) one, and now the communication is not stuck anymore. Thank you Damian229 for your time !