Skip to content

Commit

Permalink
Modbus Elements: add JUnit tests (#2283)
Browse files Browse the repository at this point in the history
This PR adds JUnit tests for all Modbus Elements (BitsWordElement, FloatQuadruplewordElement, etc.), to make sure that future PRs (e.g. #1976 and #2273) do not add regressions to the current state
  • Loading branch information
sfeilmeier authored Jul 25, 2023
1 parent 52f34d3 commit f7fa836
Show file tree
Hide file tree
Showing 17 changed files with 1,096 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public DummyModbusBridge(String id) {
for (Channel<?> channel : this.channels()) {
channel.nextProcessImage();
}
super.activate(null, id, "", true, LogVerbosity.NONE, 1);
super.activate(null, id, "", true, LogVerbosity.NONE, 2);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,32 @@

import io.openems.common.exceptions.OpenemsException;
import io.openems.common.utils.ConfigUtils;
import io.openems.edge.bridge.modbus.api.AbstractModbusBridge;
import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
import io.openems.edge.bridge.modbus.api.BridgeModbus;
import io.openems.edge.bridge.modbus.api.ModbusComponent;
import io.openems.edge.bridge.modbus.api.ModbusProtocol;
import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
import io.openems.edge.common.channel.Channel;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.test.DummyComponentContext;
import io.openems.edge.common.test.DummyConfigurationAdmin;
import io.openems.edge.common.test.DummyConfigurationAdmin.DummyConfiguration;

public abstract class DummyModbusComponent extends AbstractOpenemsModbusComponent implements ModbusComponent {
public class DummyModbusComponent extends AbstractOpenemsModbusComponent implements ModbusComponent {

public DummyModbusComponent(String id, AbstractModbusBridge bridge, int unitId,
public static final String DEFAULT_COMPONENT_ID = "device0";
public static final String DEFAULT_BRIDGE_ID = "modbus0";
public static final int DEFAULT_UNIT_ID = 1;

public DummyModbusComponent() throws OpenemsException {
this(DEFAULT_COMPONENT_ID, DEFAULT_BRIDGE_ID);
}

public DummyModbusComponent(String id, String bridgeId) throws OpenemsException {
this(id, new DummyModbusBridge(bridgeId), DEFAULT_UNIT_ID, new io.openems.edge.common.channel.ChannelId[0]);
}

public DummyModbusComponent(String id, BridgeModbus bridge, int unitId,
io.openems.edge.common.channel.ChannelId[] additionalChannelIds) throws OpenemsException {
super(//
OpenemsComponent.ChannelId.values(), //
Expand All @@ -37,7 +50,18 @@ public DummyModbusComponent(String id, AbstractModbusBridge bridge, int unitId,
super.activate(context, id, "", true, unitId, cm, "Modbus", bridge.id());
}

protected ModbusProtocol defineModbusProtocol() throws OpenemsException {
return new ModbusProtocol(this);
}

@Override
protected abstract ModbusProtocol defineModbusProtocol() throws OpenemsException;
public ModbusProtocol getModbusProtocol() throws OpenemsException {
return super.getModbusProtocol();
}

@Override
public Channel<?> addChannel(io.openems.edge.common.channel.ChannelId channelId) {
return super.addChannel(channelId);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package io.openems.edge.bridge.modbus.api.element;

import static io.openems.common.channel.AccessMode.READ_WRITE;
import static io.openems.common.types.OpenemsType.BOOLEAN;
import static io.openems.common.types.OpenemsType.INTEGER;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Optional;

import org.junit.Test;

import com.ghgande.j2mod.modbus.procimg.Register;
import com.ghgande.j2mod.modbus.procimg.SimpleRegister;

import io.openems.common.exceptions.OpenemsException;
import io.openems.common.types.OpenemsType;
import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent.BitConverter;
import io.openems.edge.common.channel.BooleanWriteChannel;
import io.openems.edge.common.channel.Channel;
import io.openems.edge.common.channel.ChannelId.ChannelIdImpl;
import io.openems.edge.common.channel.Doc;

public class BitsWordElementTest {

@Test
public void testRead() throws Exception {
var sut = generateSut();

final var channel0 = addBit(sut, 0);
final var channel1 = addBit(sut, 1);
final var channel2 = addBit(sut, 2, BitConverter.INVERT);

// TODO ByteOrder is not handled here
sut.element.setInputRegisters(new Register[] { new SimpleRegister((byte) 0x00, (byte) 0x01) });

assertTrue(channel0.getNextValue().get());
assertFalse(channel1.getNextValue().get());
assertTrue(channel2.getNextValue().get());
}

@Test
public void testWriteNone1() throws Exception {
var sut = generateSut();

addBit(sut, 0);
addBit(sut, 1);
addBit(sut, 2, BitConverter.INVERT);
addBit(sut, 3);

assertEquals(Optional.empty(), sut.element.getNextWriteValueAndReset());
}

@Test
public void testWriteNone2() throws Exception {
var sut = generateSut();

final var channel0 = addBit(sut, 0);
final var channel1 = addBit(sut, 1);
final var channel2 = addBit(sut, 2, BitConverter.INVERT);
addBit(sut, 3);

channel0.setNextWriteValue(false);
channel1.setNextWriteValue(true);
channel2.setNextWriteValue(false);

System.out.println("NOTE: the following IllegalArgumentException is expected");
assertEquals(Optional.empty(), sut.element.getNextWriteValueAndReset());
}

@Test
public void testWriteBigEndian() throws Exception {
var sut = generateSut();

final var channel0 = addBit(sut, 0);
final var channel1 = addBit(sut, 1);
final var channel2 = addBit(sut, 2, BitConverter.INVERT);
final var channel8 = addBit(sut, 8);

channel0.setNextWriteValue(false);
channel1.setNextWriteValue(true);
channel2.setNextWriteValue(false);
channel8.setNextWriteValue(true);

var registers = sut.element.getNextWriteValueAndReset().get();
assertArrayEquals(new byte[] { (byte) 0x01, (byte) 0x06 }, registers[0].toBytes());
}

@Test
public void testWriteLittleEndian() throws Exception {
var sut = generateSut();
sut.element.byteOrder(LITTLE_ENDIAN);

final var channel0 = addBit(sut, 0);
final var channel1 = addBit(sut, 1);
final var channel2 = addBit(sut, 2, BitConverter.INVERT);
final var channel8 = addBit(sut, 8);

channel0.setNextWriteValue(false);
channel1.setNextWriteValue(true);
channel2.setNextWriteValue(false);
channel8.setNextWriteValue(true);

var registers = sut.element.getNextWriteValueAndReset().get();
assertArrayEquals(new byte[] { (byte) 0x06, (byte) 0x01 }, registers[0].toBytes());
}

@Test(expected = IllegalArgumentException.class)
public void testHighIndex() throws Exception {
var sut = generateSut();
addBit(sut, 16);
}

@Test(expected = IllegalArgumentException.class)
public void testLowIndex() throws Exception {
var sut = generateSut();
addBit(sut, -1);
}

@Test(expected = IllegalArgumentException.class)
public void testNotBoolean() throws Exception {
var sut = generateSut();
addBit(sut, 0, null, OpenemsType.INTEGER);
}

private static ModbusTest.FC3ReadRegisters<BitsWordElement, ?> generateSut() throws IllegalArgumentException,
IllegalAccessException, OpenemsException, NoSuchFieldException, SecurityException {
var sut = new ModbusTest.FC3ReadRegisters<>(new BitsWordElement(0, null), INTEGER);

// Some Reflection to properly initialize the BitsWordElement
var field = BitsWordElement.class.getDeclaredField("component");
field.setAccessible(true);
field.set(sut.element, sut);

return sut;
}

private static BooleanWriteChannel addBit(ModbusTest.FC3ReadRegisters<BitsWordElement, ?> sut, int i) {
return addBit(sut, i, null);
}

private static BooleanWriteChannel addBit(ModbusTest.FC3ReadRegisters<BitsWordElement, ?> sut, int i,
BitConverter bitConverter) {
return addBit(sut, i, bitConverter, BOOLEAN);
}

private static <T extends Channel<?>> T addBit(ModbusTest.FC3ReadRegisters<BitsWordElement, ?> sut, int i,
BitConverter bitConverter, OpenemsType openemsType) {
var channelId = new ChannelIdImpl("CHANNEL_" + i, Doc.of(openemsType).accessMode(READ_WRITE));
@SuppressWarnings("unchecked")
var channel = (T) sut.addChannel(channelId);
if (bitConverter != null) {
sut.element.bit(i, channelId, bitConverter);
} else {
sut.element.bit(i, channelId);
}
return channel;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.openems.edge.bridge.modbus.api.element;

import static io.openems.common.types.OpenemsType.BOOLEAN;
import static org.junit.Assert.assertEquals;

import java.util.Optional;

import org.junit.Test;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.exceptions.OpenemsException;

public class CoilElementTest {

@Test
public void testRead() throws OpenemsException {
var sut = new ModbusTest.FC1ReadCoils<>(new CoilElement(0), BOOLEAN);
sut.element.setInputCoil(true);
assertEquals(true, sut.channel.getNextValue().get());

sut.element.setInputCoil(false);
assertEquals(false, sut.channel.getNextValue().get());

sut.element.setInputCoil(null);
assertEquals(null, sut.channel.getNextValue().get());
}

@Test
public void testWrite() throws IllegalArgumentException, OpenemsNamedException {
var sut = new ModbusTest.FC5WriteCoil<>(new CoilElement(0), BOOLEAN);
sut.channel.setNextWriteValueFromObject(true);
assertEquals(true, ((CoilElement) sut.element).getNextWriteValueAndReset().get());

sut.channel.setNextWriteValueFromObject(null);
assertEquals(Optional.empty(), ((CoilElement) sut.element).getNextWriteValueAndReset());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.openems.edge.bridge.modbus.api.element;

import org.junit.Test;

public class DummyCoilElementTest {

@Test
public void test() {
new DummyCoilElement(0);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.openems.edge.bridge.modbus.api.element;

import org.junit.Test;

public class DummyRegisterElementTest {

@Test
public void test() {
new DummyRegisterElement(0);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package io.openems.edge.bridge.modbus.api.element;

import static io.openems.common.types.OpenemsType.FLOAT;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import org.junit.Test;

import com.ghgande.j2mod.modbus.procimg.Register;
import com.ghgande.j2mod.modbus.procimg.SimpleRegister;

import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.exceptions.OpenemsException;

public class FloatDoublewordElementTest {

@Test
public void testReadBigEndianMswLsw() throws OpenemsException {
var sut = new ModbusTest.FC3ReadRegisters<>(//
new FloatDoublewordElement(0), //
FLOAT);
sut.element.setInputRegisters(new Register[] { //
new SimpleRegister((byte) 0x44, (byte) 0x9A), //
new SimpleRegister((byte) 0x51, (byte) 0xEC) //
});
assertEquals(1234.56F, (float) sut.channel.getNextValue().get(), 0.001F);
}

@Test
public void testReadBigEndianLswMsw() throws OpenemsException {
var sut = new ModbusTest.FC3ReadRegisters<>(//
new FloatDoublewordElement(0).wordOrder(WordOrder.LSWMSW), //
FLOAT);
sut.element.setInputRegisters(new Register[] { //
new SimpleRegister((byte) 0x51, (byte) 0xEC), //
new SimpleRegister((byte) 0x44, (byte) 0x9A) //
});
assertEquals(1234.56F, (float) sut.channel.getNextValue().get(), 0.001F);
}

@Test
public void testReadLittleEndianMswLsw() throws OpenemsException {
var sut = new ModbusTest.FC3ReadRegisters<>(//
new FloatDoublewordElement(0).byteOrder(LITTLE_ENDIAN), //
FLOAT);
sut.element.setInputRegisters(new Register[] { //
new SimpleRegister((byte) 0xEC, (byte) 0x51), //
new SimpleRegister((byte) 0x9A, (byte) 0x44) //
});
assertEquals(1234.56F, (float) sut.channel.getNextValue().get(), 0.001F);
}

@Test
public void testReadLittleEndianlswMsw() throws OpenemsException {
var sut = new ModbusTest.FC3ReadRegisters<>(//
new FloatDoublewordElement(0).byteOrder(LITTLE_ENDIAN).wordOrder(WordOrder.LSWMSW), //
FLOAT);
sut.element.setInputRegisters(new Register[] { //
new SimpleRegister((byte) 0x9A, (byte) 0x44), //
new SimpleRegister((byte) 0xEC, (byte) 0x51) //
});
assertEquals(1234.56F, (float) sut.channel.getNextValue().get(), 0.001F);
}

@Test
public void testWrite() throws IllegalArgumentException, OpenemsNamedException {
var sut = new ModbusTest.FC16WriteRegisters<>(//
new FloatDoublewordElement(0), //
FLOAT);
sut.channel.setNextWriteValueFromObject(1234.56F);
var registers = sut.element.getNextWriteValueAndReset().get();
assertArrayEquals(new byte[] { (byte) 0x44, (byte) 0x9A }, registers[0].toBytes());
assertArrayEquals(new byte[] { (byte) 0x51, (byte) 0xEC }, registers[1].toBytes());
}

}
Loading

0 comments on commit f7fa836

Please sign in to comment.