GamepadController.java

1
package com.rox.input;
2
3
import de.gurkenlabs.input4j.InputDevice;
4
5
import java.util.Map;
6
7
/**
8
 * A {@link Controller} driven by a real physical gamepad via input4j's {@link InputDevice}. Unlike
9
 * {@link KeyboardController} (event-driven from the EDT), a gamepad has no push-based events - it must
10
 * be actively polled, which {@link #poll()} does once per emulated video frame (see {@code NES}'s
11
 * frame-ready clock listener), not once per {@code $4016}/{@code $4017} read - polling a real device is
12
 * comparatively expensive, and the NES only ever needs a snapshot from the start of the frame.
13
 *
14
 * {@link #resolvePressedMask} is deliberately extracted as a pure, directly-testable seam: it only
15
 * reads already-polled {@link de.gurkenlabs.input4j.InputComponent} data (plain Java, no native calls),
16
 * so - unlike {@code SpeakerAudioOutput}'s real hardware line - nothing about this class actually needs
17
 * excluding from coverage; {@link InputDevice#poll()} itself is exercised in tests via a fake,
18
 * injectable poll callback (matching how {@code InputDevice} was designed to be constructed).
19
 */
20
public final class GamepadController implements Controller {
21
    private final InputDevice device;
22
    private final Map<Button, GamepadBinding> bindings;
23
    //written only by poll() (the frame-ready clock listener thread), read only by isPressed() (the
24
    //CPU thread during $4016/$4017 reads) - mirrors KeyboardController's own single-writer/single-reader
25
    //volatile mask, just with a different pair of threads
26
    private volatile int pressedMask;
27
28
    public GamepadController(final InputDevice device, final Map<Button, GamepadBinding> bindings){
29
        this.device = device;
30
        this.bindings = bindings;
31
    }
32
33
    @Override
34
    public void poll(){
35 1 1. poll : removed call to de/gurkenlabs/input4j/InputDevice::poll → KILLED
        device.poll();
36
        pressedMask = resolvePressedMask(bindings, device);
37
    }
38
39
    static int resolvePressedMask(final Map<Button, GamepadBinding> bindings, final InputDevice device){
40
        int mask = 0;
41
        for (final Map.Entry<Button, GamepadBinding> entry : bindings.entrySet()){
42 1 1. resolvePressedMask : negated conditional → KILLED
            if (entry.getValue().isPressed(device)){
43 2 1. resolvePressedMask : Replaced bitwise OR with AND → KILLED
2. resolvePressedMask : Replaced Shift Left with Shift Right → KILLED
                mask |= 1 << entry.getKey().ordinal();
44
            }
45
        }
46 1 1. resolvePressedMask : replaced int return with 0 for com/rox/input/GamepadController::resolvePressedMask → KILLED
        return mask;
47
    }
48
49
    @Override
50
    public boolean isPressed(final Button button){
51 4 1. isPressed : Replaced Shift Left with Shift Right → KILLED
2. isPressed : replaced boolean return with true for com/rox/input/GamepadController::isPressed → KILLED
3. isPressed : Replaced bitwise AND with OR → KILLED
4. isPressed : negated conditional → KILLED
        return (pressedMask & (1 << button.ordinal())) != 0;
52
    }
53
}

Mutations

35

1.1
Location : poll
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:axisBindingIsPressedExactlyAtItsPositiveThreshold()]
removed call to de/gurkenlabs/input4j/InputDevice::poll → KILLED

42

1.1
Location : resolvePressedMask
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:aBindingForAComponentTheDeviceDoesNotHaveIsNeverPressedRatherThanThrowing()]
negated conditional → KILLED

43

1.1
Location : resolvePressedMask
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:pollReadsABoundAxisComponentPastItsThreshold()]
Replaced bitwise OR with AND → KILLED

2.2
Location : resolvePressedMask
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:pollReadsABoundAxisComponentPastItsThreshold()]
Replaced Shift Left with Shift Right → KILLED

46

1.1
Location : resolvePressedMask
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:axisBindingIsPressedExactlyAtItsPositiveThreshold()]
replaced int return with 0 for com/rox/input/GamepadController::resolvePressedMask → KILLED

51

1.1
Location : isPressed
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:axisBindingIsPressedExactlyAtItsPositiveThreshold()]
Replaced Shift Left with Shift Right → KILLED

2.2
Location : isPressed
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:aBindingForAComponentTheDeviceDoesNotHaveIsNeverPressedRatherThanThrowing()]
replaced boolean return with true for com/rox/input/GamepadController::isPressed → KILLED

3.3
Location : isPressed
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:aBindingForAComponentTheDeviceDoesNotHaveIsNeverPressedRatherThanThrowing()]
Replaced bitwise AND with OR → KILLED

4.4
Location : isPressed
Killed by : com.rox.input.GamepadControllerTest.[engine:junit-jupiter]/[class:com.rox.input.GamepadControllerTest]/[method:aBindingForAComponentTheDeviceDoesNotHaveIsNeverPressedRatherThanThrowing()]
negated conditional → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1