Skip to content

Commit baaf180

Browse files
fix: Support negative steps/floats for Rotate, etc
1 parent 9063bd6 commit baaf180

2 files changed

Lines changed: 120 additions & 7 deletions

File tree

buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceFeature.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,16 @@ private void checkStepRange(final ButtplugOutput type, final int value) throws B
130130
throw new ButtplugDeviceFeatureException(type);
131131
}
132132
if (desc instanceof DeviceFeature.SteppedOutputDescriptor) {
133-
int steps = ((DeviceFeature.SteppedOutputDescriptor) desc).getValue()[1];
134-
if (value > steps || value < 0) {
135-
throw new ButtplugDeviceFeatureException(value, steps);
133+
if (value < 0) {
134+
int steps = ((DeviceFeature.SteppedOutputDescriptor) desc).getValue()[0];
135+
if (value < steps) {
136+
throw new ButtplugDeviceFeatureException(value, steps);
137+
}
138+
} else {
139+
int steps = ((DeviceFeature.SteppedOutputDescriptor) desc).getValue()[1];
140+
if (value > steps) {
141+
throw new ButtplugDeviceFeatureException(value, steps);
142+
}
136143
}
137144
} else {
138145
throw new ButtplugDeviceFeatureException(type);
@@ -339,9 +346,28 @@ public boolean hasHwPositionWithDuration() {
339346
public Future<ButtplugMessage> runHwPositionWithDuration(final int position, final int duration)
340347
throws ButtplugDeviceFeatureException {
341348
checkStepRange(ButtplugOutput.HW_POSITION_WITH_DURATION, position);
349+
350+
// Validate duration
351+
checkDuration(duration);
352+
342353
return device.runOutput(featureIndex, new OutputCmd.HwPositionWithDuration(position, duration));
343354
}
344355

356+
private void checkDuration(final int duration) throws ButtplugDeviceFeatureException {
357+
DeviceFeature.OutputDescriptor desc = output.get(ButtplugOutput.HW_POSITION_WITH_DURATION);
358+
if (desc == null) {
359+
throw new ButtplugDeviceFeatureException(ButtplugOutput.HW_POSITION_WITH_DURATION);
360+
}
361+
if (desc instanceof DeviceFeature.HwPositionWithDuration) {
362+
int steps = ((DeviceFeature.HwPositionWithDuration) desc).getDuration()[1];
363+
if (duration > steps || duration < 0) {
364+
throw new ButtplugDeviceFeatureException(duration, steps);
365+
}
366+
} else {
367+
throw new ButtplugDeviceFeatureException(ButtplugOutput.HW_POSITION_WITH_DURATION);
368+
}
369+
}
370+
345371
/**
346372
* Run HW position with duration.
347373
*
@@ -352,9 +378,13 @@ public Future<ButtplugMessage> runHwPositionWithDuration(final int position, fin
352378
*/
353379
public Future<ButtplugMessage> runHwPositionWithDurationFloat(final float position, final int duration)
354380
throws ButtplugDeviceFeatureException {
355-
int steps = getStepFromFloat(ButtplugOutput.HW_POSITION_WITH_DURATION, position);
356-
checkStepRange(ButtplugOutput.HW_POSITION_WITH_DURATION, steps);
357-
return device.runOutput(featureIndex, new OutputCmd.HwPositionWithDuration(steps, duration));
381+
int step = getStepFromFloat(ButtplugOutput.HW_POSITION_WITH_DURATION, position);
382+
checkStepRange(ButtplugOutput.HW_POSITION_WITH_DURATION, step);
383+
// Validate duration
384+
checkDuration(duration);
385+
386+
checkStepRange(ButtplugOutput.HW_POSITION_WITH_DURATION, step);
387+
return device.runOutput(featureIndex, new OutputCmd.HwPositionWithDuration(step, duration));
358388
}
359389

360390
/**

buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceFeatureTest.java

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.concurrent.Future;
1414

1515
import static org.junit.jupiter.api.Assertions.*;
16+
import static org.junit.jupiter.api.Assertions.assertEquals;
1617
import static org.mockito.ArgumentMatchers.*;
1718
import static org.mockito.Mockito.*;
1819

@@ -33,7 +34,7 @@ void setup() {
3334

3435
ArrayList<DeviceFeature.OutputDescriptor> outputs = new ArrayList<>();
3536
outputs.add(new DeviceFeature.Vibrate(new int[]{0, 100}));
36-
outputs.add(new DeviceFeature.Rotate(new int[]{0, 50}));
37+
outputs.add(new DeviceFeature.Rotate(new int[]{-50, 50}));
3738
outputs.add(new DeviceFeature.Oscillate(new int[]{0, 20}));
3839
outputs.add(new DeviceFeature.Constrict(new int[]{0, 10}));
3940
outputs.add(new DeviceFeature.Spray(new int[]{0, 5}));
@@ -67,6 +68,88 @@ void testVibrateWithValidStep() throws Exception {
6768
assertEquals(50, ((OutputCmd.Vibrate) captor.getValue()).getValue());
6869
}
6970

71+
@Test
72+
void testNegativeVibrateSteps() throws Exception {
73+
CompletableFuture<ButtplugMessage> future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
74+
when(mockDevice.runOutput(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
75+
76+
try {
77+
Future<ButtplugMessage> result = clientFeature.runVibrateFloat(-1.0f);
78+
fail("Shouldn't get here");
79+
} catch (ButtplugDeviceFeatureException e) {
80+
assertEquals("Buttplug Device Feature value out of range", e.getMessage());
81+
}
82+
}
83+
84+
@Test
85+
void testNegativeVibrateFloat() throws Exception {
86+
CompletableFuture<ButtplugMessage> future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
87+
when(mockDevice.runOutput(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
88+
89+
try {
90+
Future<ButtplugMessage> result = clientFeature.runVibrateFloat(-1.0f);
91+
fail("Shouldn't get here");
92+
} catch (ButtplugDeviceFeatureException e) {
93+
assertEquals("Buttplug Device Feature value out of range", e.getMessage());
94+
}
95+
}
96+
97+
98+
@Test
99+
void testNegativeRotateSteps() throws Exception {
100+
CompletableFuture<ButtplugMessage> future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
101+
when(mockDevice.runOutput(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
102+
103+
Future<ButtplugMessage> result = clientFeature.runRotate(-50);
104+
105+
assertNotNull(result);
106+
ArgumentCaptor<OutputCmd.IOutputCommand> captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
107+
verify(mockDevice).runOutput(eq(0), captor.capture());
108+
assertInstanceOf(OutputCmd.Rotate.class, captor.getValue());
109+
assertEquals(-50, ((OutputCmd.Rotate) captor.getValue()).getValue());
110+
}
111+
112+
@Test
113+
void testNegativeRotateFloat() throws Exception {
114+
CompletableFuture<ButtplugMessage> future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
115+
when(mockDevice.runOutput(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
116+
117+
Future<ButtplugMessage> result = clientFeature.runRotateFloat(-1.0f);
118+
119+
assertNotNull(result);
120+
ArgumentCaptor<OutputCmd.IOutputCommand> captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
121+
verify(mockDevice).runOutput(eq(0), captor.capture());
122+
assertInstanceOf(OutputCmd.Rotate.class, captor.getValue());
123+
assertEquals(-50, ((OutputCmd.Rotate) captor.getValue()).getValue());
124+
}
125+
126+
@Test
127+
void testNegativeRotateStepsOOR() throws Exception {
128+
CompletableFuture<ButtplugMessage> future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
129+
when(mockDevice.runOutput(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
130+
131+
try {
132+
Future<ButtplugMessage> result = clientFeature.runVibrateFloat(-1.0f);
133+
fail("Shouldn't get here");
134+
} catch (ButtplugDeviceFeatureException e) {
135+
assertEquals("Buttplug Device Feature value out of range", e.getMessage());
136+
}
137+
}
138+
139+
@Test
140+
void testNegativeRotateFloatOOR() throws Exception {
141+
CompletableFuture<ButtplugMessage> future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
142+
when(mockDevice.runOutput(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
143+
144+
try {
145+
Future<ButtplugMessage> result = clientFeature.runVibrateFloat(-1.0f);
146+
fail("Shouldn't get here");
147+
} catch (ButtplugDeviceFeatureException e) {
148+
assertEquals("Buttplug Device Feature value out of range", e.getMessage());
149+
}
150+
}
151+
152+
70153
@Test
71154
void testVibrateWithInvalidStepThrowsException() {
72155
assertThrows(ButtplugDeviceFeatureException.class, () -> clientFeature.runVibrate(150));

0 commit comments

Comments
 (0)