From eedc812be76c7b475c8e9d77509c700a736758ae Mon Sep 17 00:00:00 2001 From: atlanticbomber Date: Wed, 10 Sep 2025 15:06:41 -0700 Subject: [PATCH 1/5] Attempted to add the ParametrizedIntakeSubsystem.java from Robot 2025 to this repository so we can use it in future bots. Currently does not compile --- lib/build.gradle | 3 + .../ParametrizedIntakeSubsystem.java | 106 ++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java diff --git a/lib/build.gradle b/lib/build.gradle index 40ce8c1b..f3877044 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -33,6 +33,9 @@ dependencies { testRuntimeOnly('org.junit.vintage:junit-vintage-engine') testImplementation 'junit:junit:4.13.2' testImplementation 'com.google.truth:truth:1.4.4' + + compileOnly 'com.google.auto.value:auto-value-annotations:1.11.0' + annotationProcessor 'com.google.auto.value:auto-value:1.11.0' } // the magic line that makes tests work :) diff --git a/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java b/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java new file mode 100644 index 00000000..c0e69e80 --- /dev/null +++ b/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java @@ -0,0 +1,106 @@ +package com.team2813.lib2813.subsystems; +import com.google.auto.value.AutoBuilder; +import com.team2813.lib2813.control.ControlMode; +import com.team2813.lib2813.control.PIDMotor; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.InstantCommand; +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +abstract class ParameterizedIntakeSubsystem extends SubsystemBase implements AutoCloseable { + private final PIDMotor intakeMotor; + private final Params params; + + public record Params(ControlMode controlMode, double intakeDemand, double outtakeDemand) { + + public static Params.Builder builder() { + return new AutoBuilder_ParameterizedIntakeSubsystem_Params_Builder() + .setControlMode(ControlMode.VOLTAGE); + } + + @AutoBuilder + public interface Builder { + Builder setControlMode(ControlMode controlMode); + + Builder setIntakeDemand(double demand); + + Builder setOuttakeDemand(double demand); + + Params build(); + } + + public Params { + if (controlMode == null) { + throw new IllegalArgumentException("controlMode cannot be null"); + } + if (isEssentiallyZero(intakeDemand)) { + throw new IllegalArgumentException("intakeDemand cannot be zero"); + } + if (isEssentiallyZero(outtakeDemand)) { + throw new IllegalArgumentException("outtakeDemand cannot be zero"); + } + if (Math.signum(intakeDemand) == Math.signum(outtakeDemand)) { + throw new IllegalArgumentException( + "intakeDemand should be the opposite sign of outtakeDemand"); + } + } + } + + protected ParameterizedIntakeSubsystem(PIDMotor intakeMotor, Params params) { + this.intakeMotor = intakeMotor; + this.params = params; + } + + public final Command intakeItemCommand() { + return new InstantCommand(this::intakeGamePiece, this); + } + + public final Command outtakeItemCommand() { + return new InstantCommand(this::outtakeGamePiece, this); + } + + public final Command stopMotorCommand() { + return new InstantCommand(this::stopMotor, this); + } + + /** Makes intake wheels spin in the intake direction. */ + protected final void intakeGamePiece() { + // FIXME: Maybe add a check that the wheels are not stalled. + setMotorDemand(params.intakeDemand); + } + + /** Makes intake wheels spin in the outtake direction. */ + protected final void outtakeGamePiece() { + setMotorDemand(params.outtakeDemand); + } + + /** + * Runs the motor with the provided demand value. + * + * @param demand Demand of the motor. Meaning depends on the {@code ControlMode}. + */ + protected final void setMotorDemand(double demand) { + intakeMotor.set(params.controlMode, demand); + } + + /** + * Returns a command that runs the motor with the provided demand value. + * + * @param demand Demand of the motor. Meaning depends on the {@code ControlMode}. + */ + protected final Command setMotorDemandCommand(double demand) { + return new InstantCommand(() -> setMotorDemand(demand), this); + } + + /** Stops the motor. */ + public final void stopMotor() { + setMotorDemand(0.0); + } + + @Override + public void close() {} + + private static boolean isEssentiallyZero(double value) { + return Math.abs(value) < 0.001; + } +} + From ab19fc762f1ae4284af07a50a50eb34e6fcbedba Mon Sep 17 00:00:00 2001 From: cuttestkittensrule Date: Wed, 10 Sep 2025 16:03:51 -0700 Subject: [PATCH 2/5] run the formatter --- .../ParametrizedIntakeSubsystem.java | 172 +++++++++--------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java b/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java index c0e69e80..4da1a966 100644 --- a/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java +++ b/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java @@ -1,4 +1,5 @@ package com.team2813.lib2813.subsystems; + import com.google.auto.value.AutoBuilder; import com.team2813.lib2813.control.ControlMode; import com.team2813.lib2813.control.PIDMotor; @@ -7,100 +8,99 @@ import edu.wpi.first.wpilibj2.command.SubsystemBase; abstract class ParameterizedIntakeSubsystem extends SubsystemBase implements AutoCloseable { - private final PIDMotor intakeMotor; - private final Params params; - - public record Params(ControlMode controlMode, double intakeDemand, double outtakeDemand) { - - public static Params.Builder builder() { - return new AutoBuilder_ParameterizedIntakeSubsystem_Params_Builder() - .setControlMode(ControlMode.VOLTAGE); - } - - @AutoBuilder - public interface Builder { - Builder setControlMode(ControlMode controlMode); - - Builder setIntakeDemand(double demand); - - Builder setOuttakeDemand(double demand); - - Params build(); - } - - public Params { - if (controlMode == null) { - throw new IllegalArgumentException("controlMode cannot be null"); - } - if (isEssentiallyZero(intakeDemand)) { - throw new IllegalArgumentException("intakeDemand cannot be zero"); - } - if (isEssentiallyZero(outtakeDemand)) { - throw new IllegalArgumentException("outtakeDemand cannot be zero"); - } - if (Math.signum(intakeDemand) == Math.signum(outtakeDemand)) { - throw new IllegalArgumentException( - "intakeDemand should be the opposite sign of outtakeDemand"); - } - } - } - - protected ParameterizedIntakeSubsystem(PIDMotor intakeMotor, Params params) { - this.intakeMotor = intakeMotor; - this.params = params; - } - - public final Command intakeItemCommand() { - return new InstantCommand(this::intakeGamePiece, this); - } - - public final Command outtakeItemCommand() { - return new InstantCommand(this::outtakeGamePiece, this); - } + private final PIDMotor intakeMotor; + private final Params params; - public final Command stopMotorCommand() { - return new InstantCommand(this::stopMotor, this); - } + public record Params(ControlMode controlMode, double intakeDemand, double outtakeDemand) { - /** Makes intake wheels spin in the intake direction. */ - protected final void intakeGamePiece() { - // FIXME: Maybe add a check that the wheels are not stalled. - setMotorDemand(params.intakeDemand); + public static Params.Builder builder() { + return new AutoBuilder_ParameterizedIntakeSubsystem_Params_Builder() + .setControlMode(ControlMode.VOLTAGE); } - /** Makes intake wheels spin in the outtake direction. */ - protected final void outtakeGamePiece() { - setMotorDemand(params.outtakeDemand); - } + @AutoBuilder + public interface Builder { + Builder setControlMode(ControlMode controlMode); - /** - * Runs the motor with the provided demand value. - * - * @param demand Demand of the motor. Meaning depends on the {@code ControlMode}. - */ - protected final void setMotorDemand(double demand) { - intakeMotor.set(params.controlMode, demand); - } + Builder setIntakeDemand(double demand); - /** - * Returns a command that runs the motor with the provided demand value. - * - * @param demand Demand of the motor. Meaning depends on the {@code ControlMode}. - */ - protected final Command setMotorDemandCommand(double demand) { - return new InstantCommand(() -> setMotorDemand(demand), this); - } + Builder setOuttakeDemand(double demand); - /** Stops the motor. */ - public final void stopMotor() { - setMotorDemand(0.0); + Params build(); } - @Override - public void close() {} - - private static boolean isEssentiallyZero(double value) { - return Math.abs(value) < 0.001; + public Params { + if (controlMode == null) { + throw new IllegalArgumentException("controlMode cannot be null"); + } + if (isEssentiallyZero(intakeDemand)) { + throw new IllegalArgumentException("intakeDemand cannot be zero"); + } + if (isEssentiallyZero(outtakeDemand)) { + throw new IllegalArgumentException("outtakeDemand cannot be zero"); + } + if (Math.signum(intakeDemand) == Math.signum(outtakeDemand)) { + throw new IllegalArgumentException( + "intakeDemand should be the opposite sign of outtakeDemand"); + } } + } + + protected ParameterizedIntakeSubsystem(PIDMotor intakeMotor, Params params) { + this.intakeMotor = intakeMotor; + this.params = params; + } + + public final Command intakeItemCommand() { + return new InstantCommand(this::intakeGamePiece, this); + } + + public final Command outtakeItemCommand() { + return new InstantCommand(this::outtakeGamePiece, this); + } + + public final Command stopMotorCommand() { + return new InstantCommand(this::stopMotor, this); + } + + /** Makes intake wheels spin in the intake direction. */ + protected final void intakeGamePiece() { + // FIXME: Maybe add a check that the wheels are not stalled. + setMotorDemand(params.intakeDemand); + } + + /** Makes intake wheels spin in the outtake direction. */ + protected final void outtakeGamePiece() { + setMotorDemand(params.outtakeDemand); + } + + /** + * Runs the motor with the provided demand value. + * + * @param demand Demand of the motor. Meaning depends on the {@code ControlMode}. + */ + protected final void setMotorDemand(double demand) { + intakeMotor.set(params.controlMode, demand); + } + + /** + * Returns a command that runs the motor with the provided demand value. + * + * @param demand Demand of the motor. Meaning depends on the {@code ControlMode}. + */ + protected final Command setMotorDemandCommand(double demand) { + return new InstantCommand(() -> setMotorDemand(demand), this); + } + + /** Stops the motor. */ + public final void stopMotor() { + setMotorDemand(0.0); + } + + @Override + public void close() {} + + private static boolean isEssentiallyZero(double value) { + return Math.abs(value) < 0.001; + } } - From 5a4319528255f2253efe4cd95a1fe6590f150e46 Mon Sep 17 00:00:00 2001 From: atlanticbomber Date: Thu, 11 Sep 2025 11:03:05 -0700 Subject: [PATCH 3/5] Fixed File naming error and made ParameterizedIntakeSubsystem public --- ...edIntakeSubsystem.java => ParameterizedIntakeSubsystem.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename lib/src/main/java/com/team2813/lib2813/subsystems/{ParametrizedIntakeSubsystem.java => ParameterizedIntakeSubsystem.java} (96%) diff --git a/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java b/lib/src/main/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystem.java similarity index 96% rename from lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java rename to lib/src/main/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystem.java index 4da1a966..7faeeb00 100644 --- a/lib/src/main/java/com/team2813/lib2813/subsystems/ParametrizedIntakeSubsystem.java +++ b/lib/src/main/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystem.java @@ -7,7 +7,7 @@ import edu.wpi.first.wpilibj2.command.InstantCommand; import edu.wpi.first.wpilibj2.command.SubsystemBase; -abstract class ParameterizedIntakeSubsystem extends SubsystemBase implements AutoCloseable { +public abstract class ParameterizedIntakeSubsystem extends SubsystemBase implements AutoCloseable { private final PIDMotor intakeMotor; private final Params params; From f30285e155c8cadad26767fc7d8b8f8f36dcc1fc Mon Sep 17 00:00:00 2001 From: atlanticbomber Date: Thu, 18 Sep 2025 11:13:22 -0700 Subject: [PATCH 4/5] added ParameterizedIntakeSubsystemTest to lib2813. --- lib/build.gradle | 3 +- .../lib2813/subsystems/FakePIDMotor.java | 40 +++++++ .../ParameterizedIntakeSubsystemTest.java | 110 ++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 lib/src/test/java/com/team2813/lib2813/subsystems/FakePIDMotor.java create mode 100644 lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java diff --git a/lib/build.gradle b/lib/build.gradle index f3877044..a0101194 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -29,11 +29,12 @@ dependencies { testImplementation(platform('org.junit:junit-bom:5.13.1')) testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation 'org.mockito:mockito-core:5.14.2' testRuntimeOnly('org.junit.platform:junit-platform-launcher') testRuntimeOnly('org.junit.vintage:junit-vintage-engine') testImplementation 'junit:junit:4.13.2' testImplementation 'com.google.truth:truth:1.4.4' - + testImplementation project(':testing') compileOnly 'com.google.auto.value:auto-value-annotations:1.11.0' annotationProcessor 'com.google.auto.value:auto-value:1.11.0' } diff --git a/lib/src/test/java/com/team2813/lib2813/subsystems/FakePIDMotor.java b/lib/src/test/java/com/team2813/lib2813/subsystems/FakePIDMotor.java new file mode 100644 index 00000000..5a43ec0b --- /dev/null +++ b/lib/src/test/java/com/team2813/lib2813/subsystems/FakePIDMotor.java @@ -0,0 +1,40 @@ +package com.team2813.lib2813.subsystems; + +import static com.google.common.truth.Truth.assertThat; + +import com.team2813.lib2813.control.ControlMode; +import com.team2813.lib2813.control.PIDMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.AngularVelocity; + +public abstract class FakePIDMotor implements PIDMotor { + double demand = 0.0f; + private ControlMode controlMode; + + double getVoltage() { + assertThat(controlMode).isEqualTo(ControlMode.VOLTAGE); + return demand; + } + + @Override + public void set(ControlMode mode, double demand) { + assertThat(mode).isNotNull(); + controlMode = mode; + this.demand = demand; + } + + @Override + public void set(ControlMode mode, double demand, double feedForward) { + set(mode, demand); + } + + @Override + public AngularVelocity getVelocityMeasure() { + return Units.RadiansPerSecond.of(demand * 20); + } + + @Override + public double getVelocity() { + throw new AssertionError("Called deprecated method"); + } +} diff --git a/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java b/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java new file mode 100644 index 00000000..69fbe510 --- /dev/null +++ b/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java @@ -0,0 +1,110 @@ +package com.team2813.lib2813.subsystems; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; + +import com.team2813.lib2813.control.ControlMode; +import com.team2813.lib2813.control.PIDMotor; +import com.team2813.lib2813.testing.junit.jupiter.CommandTester; +import com.team2813.lib2813.testing.junit.jupiter.WPILibExtension; +import edu.wpi.first.wpilibj2.command.Command; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.Answers; + +@ParameterizedClass +@EnumSource(ControlMode.class) +@ExtendWith(WPILibExtension.class) +public final class ParameterizedIntakeSubsystemTest { + + private static class ConcreteParameterizedIntakeSubsystem extends ParameterizedIntakeSubsystem { + protected ConcreteParameterizedIntakeSubsystem(PIDMotor intakeMotor, Params params) { + super(intakeMotor, params); + } + } + + final FakePIDMotor fakeMotor = mock(FakePIDMotor.class, Answers.CALLS_REAL_METHODS); + private final ParameterizedIntakeSubsystem.Params params; + + public ParameterizedIntakeSubsystemTest(ControlMode controlMode) { + params = + ParameterizedIntakeSubsystem.Params.builder() + .setControlMode(controlMode) + .setIntakeDemand(42) + .setOuttakeDemand(-3.1415) + .build(); + } + + @Test + public void initialState() { + try (var ignored = new ConcreteParameterizedIntakeSubsystem(fakeMotor, params)) { + assertMotorIsStopped(); + verifyNoInteractions(fakeMotor); + } + } + + @Test + public void intakeItem(CommandTester commandTester) { + try (var intake = new ConcreteParameterizedIntakeSubsystem(fakeMotor, params)) { + Command command = intake.intakeItemCommand(); + assertMotorIsStopped(); + + commandTester.runUntilComplete(command); + + assertThat(fakeMotor.demand).isWithin(0.01).of(params.intakeDemand()); + } + } + + @Test + public void stopAfterIntakingItem(CommandTester commandTester) { + try (var intake = new ConcreteParameterizedIntakeSubsystem(fakeMotor, params)) { + Command command = intake.intakeItemCommand(); + commandTester.runUntilComplete(command); + command = intake.stopMotorCommand(); + assertMotorIsRunning(); + + commandTester.runUntilComplete(command); + + assertMotorIsStopped(); + } + } + + @Test + public void outtakeItem(CommandTester commandTester) { + try (var intake = new ConcreteParameterizedIntakeSubsystem(fakeMotor, params)) { + intake.intakeGamePiece(); + Command command = intake.outtakeItemCommand(); + assertMotorIsRunning(); + + commandTester.runUntilComplete(command); + + assertThat(fakeMotor.demand).isWithin(0.01).of(params.outtakeDemand()); + } + } + + @Test + public void stopAfterOuttakingItem(CommandTester commandTester) { + try (var intake = new ConcreteParameterizedIntakeSubsystem(fakeMotor, params)) { + intake.intakeGamePiece(); + Command command = intake.outtakeItemCommand(); + commandTester.runUntilComplete(command); + command = intake.stopMotorCommand(); + assertMotorIsRunning(); + + commandTester.runUntilComplete(command); + + assertMotorIsStopped(); + } + } + + private void assertMotorIsStopped() { + assertThat(fakeMotor.demand).isWithin(0.01).of(0.0); + } + + private void assertMotorIsRunning() { + assertThat(fakeMotor.demand).isNotWithin(0.01).of(0.0); + } +} From b882d77cba334597a50014f7580e471f9594df1a Mon Sep 17 00:00:00 2001 From: atlanticbomber Date: Tue, 23 Sep 2025 14:38:50 -0700 Subject: [PATCH 5/5] Finished migrating ParameterizedIntakeSubsystemTest to lib2813. FakePIDMotor is now in the main library to allow for access from both lib tests and robot tests. :lib now depends on google truth library for implementation as well as testing --- lib/build.gradle | 2 +- .../com/team2813/lib2813/util}/FakePIDMotor.java | 13 ++++++------- .../ParameterizedIntakeSubsystemTest.java | 1 + testing/build.gradle | 1 - 4 files changed, 8 insertions(+), 9 deletions(-) rename lib/src/{test/java/com/team2813/lib2813/subsystems => main/java/com/team2813/lib2813/util}/FakePIDMotor.java (75%) diff --git a/lib/build.gradle b/lib/build.gradle index a0101194..1d6c8e32 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -33,7 +33,7 @@ dependencies { testRuntimeOnly('org.junit.platform:junit-platform-launcher') testRuntimeOnly('org.junit.vintage:junit-vintage-engine') testImplementation 'junit:junit:4.13.2' - testImplementation 'com.google.truth:truth:1.4.4' + implementation 'com.google.truth:truth:1.4.4'//needed for FakePIDMotor testImplementation project(':testing') compileOnly 'com.google.auto.value:auto-value-annotations:1.11.0' annotationProcessor 'com.google.auto.value:auto-value:1.11.0' diff --git a/lib/src/test/java/com/team2813/lib2813/subsystems/FakePIDMotor.java b/lib/src/main/java/com/team2813/lib2813/util/FakePIDMotor.java similarity index 75% rename from lib/src/test/java/com/team2813/lib2813/subsystems/FakePIDMotor.java rename to lib/src/main/java/com/team2813/lib2813/util/FakePIDMotor.java index 5a43ec0b..a8076f8c 100644 --- a/lib/src/test/java/com/team2813/lib2813/subsystems/FakePIDMotor.java +++ b/lib/src/main/java/com/team2813/lib2813/util/FakePIDMotor.java @@ -1,24 +1,23 @@ -package com.team2813.lib2813.subsystems; - -import static com.google.common.truth.Truth.assertThat; +package com.team2813.lib2813.util; +import com.google.common.truth.Truth; import com.team2813.lib2813.control.ControlMode; import com.team2813.lib2813.control.PIDMotor; import edu.wpi.first.units.Units; import edu.wpi.first.units.measure.AngularVelocity; public abstract class FakePIDMotor implements PIDMotor { - double demand = 0.0f; + public double demand = 0.0f; private ControlMode controlMode; - double getVoltage() { - assertThat(controlMode).isEqualTo(ControlMode.VOLTAGE); + public double getVoltage() { + Truth.assertThat(controlMode).isEqualTo(ControlMode.VOLTAGE); return demand; } @Override public void set(ControlMode mode, double demand) { - assertThat(mode).isNotNull(); + Truth.assertThat(mode).isNotNull(); controlMode = mode; this.demand = demand; } diff --git a/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java b/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java index 69fbe510..9fb7a5fc 100644 --- a/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java +++ b/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java @@ -8,6 +8,7 @@ import com.team2813.lib2813.control.PIDMotor; import com.team2813.lib2813.testing.junit.jupiter.CommandTester; import com.team2813.lib2813.testing.junit.jupiter.WPILibExtension; +import com.team2813.lib2813.util.FakePIDMotor; import edu.wpi.first.wpilibj2.command.Command; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/testing/build.gradle b/testing/build.gradle index ecd2e849..2656b7db 100644 --- a/testing/build.gradle +++ b/testing/build.gradle @@ -20,7 +20,6 @@ dependencies { implementation 'com.google.truth:truth:1.4.4' implementation(platform('org.junit:junit-bom:5.13.1')) implementation('org.junit.jupiter:junit-jupiter') - nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) simulationDebug wpi.sim.enableDebug()