[hdpowerview] Fix secondary position bug. Add shade database and properties. (#11698)

Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
Andrew Fiddian-Green
2022-01-04 19:23:48 +00:00
committed by GitHub
parent 0988c040a6
commit d45bcdb7aa
14 changed files with 1193 additions and 444 deletions

View File

@@ -13,7 +13,6 @@
package org.openhab.binding.hdpowerview;
import static org.junit.jupiter.api.Assertions.*;
import static org.openhab.binding.hdpowerview.internal.api.ActuatorClass.*;
import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
import java.io.IOException;
@@ -24,13 +23,11 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.junit.jupiter.api.Test;
import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets;
import org.openhab.binding.hdpowerview.internal.HubMaintenanceException;
import org.openhab.binding.hdpowerview.internal.HubProcessingException;
import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem;
import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections;
import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection;
@@ -39,6 +36,8 @@ import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene;
import org.openhab.binding.hdpowerview.internal.api.responses.Shade;
import org.openhab.binding.hdpowerview.internal.api.responses.Shades;
import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase;
import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
@@ -47,7 +46,7 @@ import com.google.gson.Gson;
import com.google.gson.JsonParseException;
/**
* Unit tests for HD PowerView binding
* Unit tests for HD PowerView binding.
*
* @author Andrew Fiddian-Green - Initial contribution
* @author Jacob Laursen - Add support for scene groups
@@ -58,8 +57,10 @@ public class HDPowerViewJUnitTests {
private static final Pattern VALID_IP_V4_ADDRESS = Pattern
.compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
/*
* load a test JSON string from a file
* load a test JSON string from a file.
*/
private String loadJson(String fileName) {
try {
@@ -72,7 +73,7 @@ public class HDPowerViewJUnitTests {
}
/**
* Run a series of ONLINE tests on the communication with a hub
* Run a series of ONLINE tests on the communication with a hub.
*
* @param hubIPAddress must be a valid hub IP address to run the
* tests on; or an INVALID IP address to
@@ -111,79 +112,36 @@ public class HDPowerViewJUnitTests {
HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress);
assertNotNull(webTargets);
// ==== exercise some code ====
ShadePosition test;
State pos;
// shade fully up
test = ShadePosition.create(ZERO_IS_CLOSED, 0);
assertNotNull(test);
pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(0, ((PercentType) pos).intValue());
pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
assertTrue(UnDefType.UNDEF.equals(pos));
// shade fully down (method 1)
test = ShadePosition.create(ZERO_IS_CLOSED, 100);
assertNotNull(test);
pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(100, ((PercentType) pos).intValue());
pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
assertEquals(PercentType.class, pos.getClass());
assertEquals(0, ((PercentType) pos).intValue());
// shade fully down (method 2)
test = ShadePosition.create(VANE_COORDS, 0);
assertNotNull(test);
pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(100, ((PercentType) pos).intValue());
pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
assertEquals(PercentType.class, pos.getClass());
assertEquals(0, ((PercentType) pos).intValue());
// shade fully down (method 2) and vane fully open
test = ShadePosition.create(VANE_COORDS, 100);
assertNotNull(test);
pos = test.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(100, ((PercentType) pos).intValue());
pos = test.getState(PRIMARY_ACTUATOR, VANE_COORDS);
assertEquals(PercentType.class, pos.getClass());
assertEquals(100, ((PercentType) pos).intValue());
int shadeId = 0;
@Nullable
ShadePosition shadePos = null;
@Nullable
Shades shadesX = null;
// ==== get all shades ====
try {
shadesX = webTargets.getShades();
assertNotNull(shadesX);
@Nullable
List<ShadeData> shadesData = shadesX.shadeData;
assertNotNull(shadesData);
assertTrue(!shadesData.isEmpty());
@Nullable
ShadeData shadeData;
shadeData = shadesData.get(0);
assertNotNull(shadeData);
assertTrue(shadeData.getName().length() > 0);
shadePos = shadeData.positions;
assertNotNull(shadePos);
@Nullable
ShadeData shadeZero = shadesData.get(0);
assertNotNull(shadeZero);
shadeId = shadeZero.id;
assertNotEquals(0, shadeId);
if (shadesX != null) {
List<ShadeData> shadesData = shadesX.shadeData;
assertNotNull(shadesData);
for (ShadeData shadexData : shadesData) {
String shadeName = shadexData.getName();
assertNotNull(shadeName);
if (shadesData != null) {
assertTrue(!shadesData.isEmpty());
ShadeData shadeData;
shadeData = shadesData.get(0);
assertNotNull(shadeData);
assertTrue(shadeData.getName().length() > 0);
shadePos = shadeData.positions;
assertNotNull(shadePos);
ShadeData shadeZero = shadesData.get(0);
assertNotNull(shadeZero);
shadeId = shadeZero.id;
assertNotEquals(0, shadeId);
for (ShadeData shadexData : shadesData) {
String shadeName = shadexData.getName();
assertNotNull(shadeName);
}
}
}
} catch (JsonParseException | HubProcessingException | HubMaintenanceException e) {
fail(e.getMessage());
@@ -194,26 +152,29 @@ public class HDPowerViewJUnitTests {
try {
Scenes scenes = webTargets.getScenes();
assertNotNull(scenes);
@Nullable
List<Scene> scenesData = scenes.sceneData;
assertNotNull(scenesData);
assertTrue(!scenesData.isEmpty());
@Nullable
Scene sceneZero = scenesData.get(0);
assertNotNull(sceneZero);
sceneId = sceneZero.id;
assertTrue(sceneId > 0);
for (Scene scene : scenesData) {
String sceneName = scene.getName();
assertNotNull(sceneName);
if (scenes != null) {
List<Scene> scenesData = scenes.sceneData;
assertNotNull(scenesData);
if (scenesData != null) {
assertTrue(!scenesData.isEmpty());
Scene sceneZero = scenesData.get(0);
assertNotNull(sceneZero);
sceneId = sceneZero.id;
assertTrue(sceneId > 0);
for (Scene scene : scenesData) {
String sceneName = scene.getName();
assertNotNull(sceneName);
}
}
}
} catch (JsonParseException | HubProcessingException | HubMaintenanceException e) {
fail(e.getMessage());
}
// ==== refresh a specific shade ====
@Nullable
Shade shade = null;
try {
assertNotEquals(0, shadeId);
@@ -227,28 +188,53 @@ public class HDPowerViewJUnitTests {
try {
assertNotEquals(0, shadeId);
assertNotNull(shade);
@Nullable
ShadeData shadeData = shade.shade;
assertNotNull(shadeData);
ShadePosition positions = shadeData.positions;
assertNotNull(positions);
CoordinateSystem coordSys = positions.getCoordinateSystem(PRIMARY_ACTUATOR);
assertNotNull(coordSys);
if (shade != null) {
ShadeData shadeData = shade.shade;
assertNotNull(shadeData);
pos = positions.getState(PRIMARY_ACTUATOR, coordSys);
assertEquals(PercentType.class, pos.getClass());
if (shadeData != null) {
ShadePosition positions = shadeData.positions;
assertNotNull(positions);
pos = positions.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
if (positions != null) {
Integer capabilitiesValue = shadeData.capabilities;
assertNotNull(capabilitiesValue);
int position = ((PercentType) pos).intValue();
position = position + ((position <= 10) ? 5 : -5);
if (capabilitiesValue != null) {
Capabilities capabilities = db.getCapabilities(capabilitiesValue.intValue());
ShadePosition newPos = ShadePosition.create(ZERO_IS_CLOSED, position);
assertNotNull(newPos);
State pos = positions.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
if (allowShadeMovementCommands) {
webTargets.moveShade(shadeId, newPos);
int position = ((PercentType) pos).intValue();
position = position + ((position <= 10) ? 5 : -5);
ShadePosition targetPosition = new ShadePosition().setPosition(capabilities,
PRIMARY_ZERO_IS_CLOSED, position);
assertNotNull(targetPosition);
if (allowShadeMovementCommands) {
webTargets.moveShade(shadeId, targetPosition);
Shade newShade = webTargets.getShade(shadeId);
assertNotNull(newShade);
if (newShade != null) {
ShadeData newData = newShade.shade;
assertNotNull(newData);
if (newData != null) {
ShadePosition actualPosition = newData.positions;
assertNotNull(actualPosition);
if (actualPosition != null) {
assertEquals(
targetPosition.getState(capabilities, PRIMARY_ZERO_IS_CLOSED),
actualPosition.getState(capabilities, PRIMARY_ZERO_IS_CLOSED));
}
}
}
}
}
}
}
}
} catch (HubProcessingException | HubMaintenanceException e) {
fail(e.getMessage());
@@ -286,12 +272,78 @@ public class HDPowerViewJUnitTests {
}
/**
* Test generic JSON shades response
* Test parsing of ShadePosition (shade fully up).
*
*/
@Test
public void testShadePositionParsingFullyUp() {
Capabilities capabilities = db.getCapabilities(0);
ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_ZERO_IS_CLOSED, 0);
assertNotNull(test);
State pos = test.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(0, ((PercentType) pos).intValue());
pos = test.getState(capabilities, VANE_TILT_COORDS);
assertTrue(UnDefType.UNDEF.equals(pos));
}
/**
* Test parsing of ShadePosition (shade fully down (method 1)).
*
*/
@Test
public void testShadePositionParsingShadeFullyDown1() {
Capabilities capabilities = db.getCapabilities(0);
ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_ZERO_IS_CLOSED, 100);
assertNotNull(test);
State pos = test.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(100, ((PercentType) pos).intValue());
pos = test.getState(capabilities, VANE_TILT_COORDS);
assertEquals(PercentType.class, pos.getClass());
assertEquals(0, ((PercentType) pos).intValue());
}
/**
* Test parsing of ShadePosition (shade fully down (method 2)).
*
*/
@Test
public void testShadePositionParsingShadeFullyDown2() {
Capabilities capabilities = db.getCapabilities(0);
ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_COORDS, 0);
assertNotNull(test);
State pos = test.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(100, ((PercentType) pos).intValue());
pos = test.getState(capabilities, VANE_TILT_COORDS);
assertEquals(PercentType.class, pos.getClass());
assertEquals(0, ((PercentType) pos).intValue());
}
/**
* Test parsing of ShadePosition (shade fully down (method 2) and vane fully open).
*
*/
@Test
public void testShadePositionParsingShadeFullyDownVaneOpen() {
Capabilities capabilities = db.getCapabilities(0);
ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_COORDS, 100);
assertNotNull(test);
State pos = test.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(100, ((PercentType) pos).intValue());
pos = test.getState(capabilities, VANE_TILT_COORDS);
assertEquals(PercentType.class, pos.getClass());
assertEquals(100, ((PercentType) pos).intValue());
}
/**
* Test generic JSON shades response.
*/
@Test
public void shadeResponseIsParsedCorrectly() throws JsonParseException {
final Gson gson = new Gson();
@Nullable
Shades shades;
String json = loadJson("shades");
assertNotEquals("", json);
@@ -300,7 +352,7 @@ public class HDPowerViewJUnitTests {
}
/**
* Test generic JSON scene response
* Test generic JSON scene response.
*/
@Test
public void sceneResponseIsParsedCorrectly() throws JsonParseException {
@@ -308,23 +360,22 @@ public class HDPowerViewJUnitTests {
String json = loadJson("scenes");
assertNotEquals("", json);
@Nullable
Scenes scenes = gson.fromJson(json, Scenes.class);
assertNotNull(scenes);
@Nullable
List<Scene> sceneData = scenes.sceneData;
assertNotNull(sceneData);
assertEquals(4, sceneData.size());
@Nullable
Scene scene = sceneData.get(0);
assertEquals("Door Open", scene.getName());
assertEquals(18097, scene.id);
if (scenes != null) {
List<Scene> sceneData = scenes.sceneData;
assertNotNull(sceneData);
if (sceneData != null) {
assertEquals(4, sceneData.size());
Scene scene = sceneData.get(0);
assertEquals("Door Open", scene.getName());
assertEquals(18097, scene.id);
}
}
}
/**
* Test generic JSON scene collection response
* Test generic JSON scene collection response.
*/
@Test
public void sceneCollectionResponseIsParsedCorrectly() throws JsonParseException {
@@ -332,22 +383,24 @@ public class HDPowerViewJUnitTests {
String json = loadJson("sceneCollections");
assertNotEquals("", json);
@Nullable
SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class);
assertNotNull(sceneCollections);
@Nullable
List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
assertNotNull(sceneCollectionData);
assertEquals(1, sceneCollectionData.size());
@Nullable
SceneCollection sceneCollection = sceneCollectionData.get(0);
assertEquals("Børn op", sceneCollection.getName());
assertEquals(27119, sceneCollection.id);
if (sceneCollections != null) {
List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
assertNotNull(sceneCollectionData);
if (sceneCollectionData != null) {
assertEquals(1, sceneCollectionData.size());
SceneCollection sceneCollection = sceneCollectionData.get(0);
assertEquals("Børn op", sceneCollection.getName());
assertEquals(27119, sceneCollection.id);
}
}
}
/**
* Test the JSON parsing for a duette top down bottom up shade
* Test the JSON parsing for a duette top down bottom up shade.
*/
@Test
public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws JsonParseException {
@@ -355,38 +408,157 @@ public class HDPowerViewJUnitTests {
String json = loadJson("duette");
assertNotEquals("", json);
@Nullable
Shades shades = gson.fromJson(json, Shades.class);
assertNotNull(shades);
@Nullable
List<ShadeData> shadesData = shades.shadeData;
assertNotNull(shadesData);
if (shades != null) {
List<ShadeData> shadesData = shades.shadeData;
assertNotNull(shadesData);
assertEquals(1, shadesData.size());
@Nullable
ShadeData shadeData = shadesData.get(0);
assertNotNull(shadeData);
if (shadesData != null) {
assertEquals(1, shadesData.size());
ShadeData shadeData = shadesData.get(0);
assertNotNull(shadeData);
assertEquals("Gardin 1", shadeData.getName());
assertEquals(63778, shadeData.id);
assertEquals("Gardin 1", shadeData.getName());
assertEquals(63778, shadeData.id);
ShadePosition shadePos = shadeData.positions;
assertNotNull(shadePos);
assertEquals(ZERO_IS_CLOSED, shadePos.getCoordinateSystem(PRIMARY_ACTUATOR));
ShadePosition shadePos = shadeData.positions;
assertNotNull(shadePos);
State pos = shadePos.getState(PRIMARY_ACTUATOR, ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(59, ((PercentType) pos).intValue());
if (shadePos != null) {
Integer capabilitiesValue = shadeData.capabilities;
assertNotNull(capabilitiesValue);
if (capabilitiesValue != null) {
assertEquals(7, capabilitiesValue.intValue());
pos = shadePos.getState(SECONDARY_ACTUATOR, ZERO_IS_OPEN);
assertEquals(PercentType.class, pos.getClass());
assertEquals(35, ((PercentType) pos).intValue());
Capabilities capabilities = db.getCapabilities(capabilitiesValue);
pos = shadePos.getState(PRIMARY_ACTUATOR, VANE_COORDS);
assertEquals(UnDefType.class, pos.getClass());
State pos = shadePos.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
assertEquals(PercentType.class, pos.getClass());
assertEquals(59, ((PercentType) pos).intValue());
assertEquals(3, shadeData.batteryStatus);
pos = shadePos.getState(capabilities, SECONDARY_ZERO_IS_OPEN);
assertEquals(PercentType.class, pos.getClass());
assertEquals(35, ((PercentType) pos).intValue());
assertEquals(4, shadeData.signalStrength);
pos = shadePos.getState(capabilities, VANE_TILT_COORDS);
assertEquals(UnDefType.class, pos.getClass());
assertEquals(3, shadeData.batteryStatus);
assertEquals(4, shadeData.signalStrength);
assertEquals(8, shadeData.type);
assertTrue(db.isTypeInDatabase(shadeData.type));
assertTrue(db.isCapabilitiesInDatabase(capabilitiesValue.intValue()));
assertEquals(db.getType(shadeData.type).getCapabilities(), capabilitiesValue.intValue());
assertTrue(db.getCapabilities(capabilitiesValue.intValue()).supportsSecondary());
assertNotEquals(db.getType(shadeData.type).getCapabilities(), capabilitiesValue.intValue() + 1);
// ==== when changing position1, position2 value is not changed (vice-versa) ====
ShadePosition shadePosition = shadeData.positions;
assertNotNull(shadePosition);
if (shadePosition != null) {
// ==== position2 ====
State position2Old = shadePosition.getState(capabilities, SECONDARY_ZERO_IS_OPEN);
shadePosition.setPosition(capabilities, PRIMARY_ZERO_IS_CLOSED, 99);
State position2New = shadePosition.getState(capabilities, SECONDARY_ZERO_IS_OPEN);
assertEquals(PercentType.class, position2Old.getClass());
assertEquals(PercentType.class, position2New.getClass());
assertEquals(((PercentType) position2Old).intValue(),
((PercentType) position2New).intValue());
// ==== position2 ====
State position1Old = shadePosition.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
shadePosition.setPosition(capabilities, SECONDARY_ZERO_IS_OPEN, 99);
State position1New = shadePosition.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
assertEquals(PercentType.class, position1Old.getClass());
assertEquals(PercentType.class, position1New.getClass());
assertEquals(((PercentType) position1Old).intValue(),
((PercentType) position1New).intValue());
}
}
}
}
}
}
/**
* General tests of the database of known types.
*/
@Test
public void testKnownTypesDatabase() {
assertTrue(db.isTypeInDatabase(4));
assertTrue(db.isCapabilitiesInDatabase(0));
assertTrue(db.getCapabilities(6).isPrimaryStateInverted());
assertTrue(db.getCapabilities(7).supportsSecondary());
assertEquals(db.getType(4).getCapabilities(), 0);
assertEquals(db.getType(-1).getCapabilities(), -1);
assertFalse(db.isTypeInDatabase(99));
assertFalse(db.isCapabilitiesInDatabase(99));
assertFalse(db.getCapabilities(0).isPrimaryStateInverted());
assertFalse(db.getCapabilities(-1).isPrimaryStateInverted());
assertFalse(db.getCapabilities(99).isPrimaryStateInverted());
assertFalse(db.getCapabilities(0).supportsSecondary());
assertFalse(db.getCapabilities(-1).supportsSecondary());
assertFalse(db.getCapabilities(99).supportsSecondary());
}
/**
* On dual rail shades, it should not be possible to drive the upper rail below the lower rail, or vice-versa. So
* the binding code applies constraints on setting such positions. This test checks that the constraint code is
* working.
*/
@Test
public void testDualRailConstraints() {
ShadePosition shade = new ShadePosition();
Capabilities caps = db.getCapabilities(7);
// ==== OK !! primary at bottom, secondary at top ====
shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0);
assertEquals(PercentType.HUNDRED, shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
assertEquals(PercentType.ZERO, shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
// ==== OK !! primary at middle, secondary at top ====
shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 50).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0);
assertEquals(new PercentType(50), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
assertEquals(PercentType.ZERO, shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
// ==== OK !! primary at middle, secondary at middle ====
shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 50).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 50);
assertEquals(new PercentType(50), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
assertEquals(new PercentType(50), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
// ==== IMPOSSIBLE !! secondary at middle, primary above => test the constraining code ====
shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100);
shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 40).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 25);
assertEquals(new PercentType(40), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
assertEquals(new PercentType(40), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
// ==== OK !! secondary at middle, primary below ====
shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100);
shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 50).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 75);
assertEquals(new PercentType(50), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
assertEquals(new PercentType(75), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
// ==== IMPOSSIBLE !! primary at middle, secondary below => test the constraining code ====
shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100);
shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 60).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 75);
assertEquals(new PercentType(60), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
assertEquals(new PercentType(60), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
// ==== OK !! primary at middle, secondary above ====
shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100);
shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 60).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 25);
assertEquals(new PercentType(60), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
assertEquals(new PercentType(25), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
}
}