From 66bb0ffb2c2be507d6b296ea0c856235c4188025 Mon Sep 17 00:00:00 2001 From: Starlight220 <53231611+Starlight220@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:40:14 +0200 Subject: [PATCH] [examples] Add unit testing infrastructure (#4646) --- .../main/cpp/examples/UnitTest/cpp/Robot.cpp | 32 +++++++++ .../UnitTest/cpp/subsystems/Intake.cpp | 26 +++++++ .../cpp/examples/UnitTest/include/Constants.h | 26 +++++++ .../cpp/examples/UnitTest/include/Robot.h | 20 ++++++ .../UnitTest/include/subsystems/Intake.h | 24 +++++++ .../src/main/cpp/examples/examples.json | 21 +++--- .../test/cpp/examples/UnitTest/cpp/main.cpp | 17 +++++ .../UnitTest/cpp/subsystems/IntakeTest.cpp | 46 ++++++++++++ wpilibjExamples/build.gradle | 22 +++++- .../wpi/first/wpilibj/examples/examples.json | 11 +++ .../wpilibj/examples/unittest/Constants.java | 25 +++++++ .../first/wpilibj/examples/unittest/Main.java | 25 +++++++ .../wpilibj/examples/unittest/Robot.java | 41 +++++++++++ .../examples/unittest/subsystems/Intake.java | 51 ++++++++++++++ .../unittest/subsystems/IntakeTest.java | 70 +++++++++++++++++++ 15 files changed, 444 insertions(+), 13 deletions(-) create mode 100644 wpilibcExamples/src/main/cpp/examples/UnitTest/cpp/Robot.cpp create mode 100644 wpilibcExamples/src/main/cpp/examples/UnitTest/cpp/subsystems/Intake.cpp create mode 100644 wpilibcExamples/src/main/cpp/examples/UnitTest/include/Constants.h create mode 100644 wpilibcExamples/src/main/cpp/examples/UnitTest/include/Robot.h create mode 100644 wpilibcExamples/src/main/cpp/examples/UnitTest/include/subsystems/Intake.h create mode 100644 wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/main.cpp create mode 100644 wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/subsystems/IntakeTest.cpp create mode 100644 wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Constants.java create mode 100644 wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Main.java create mode 100644 wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Robot.java create mode 100644 wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/subsystems/Intake.java create mode 100644 wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/unittest/subsystems/IntakeTest.java diff --git a/wpilibcExamples/src/main/cpp/examples/UnitTest/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/UnitTest/cpp/Robot.cpp new file mode 100644 index 0000000000..9434b46158 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/UnitTest/cpp/Robot.cpp @@ -0,0 +1,32 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "Robot.h" + +/** + * This function is called periodically during operator control. + */ +void Robot::TeleopPeriodic() { + // Activate the intake while the trigger is held + if (m_joystick.GetTrigger()) { + m_intake.Activate(IntakeConstants::kIntakeSpeed); + } else { + m_intake.Activate(0); + } + + // Toggle deploying the intake when the top button is pressed + if (m_joystick.GetTop()) { + if (m_intake.IsDeployed()) { + m_intake.Retract(); + } else { + m_intake.Deploy(); + } + } +} + +#ifndef RUNNING_FRC_TESTS +int main() { + return frc::StartRobot(); +} +#endif diff --git a/wpilibcExamples/src/main/cpp/examples/UnitTest/cpp/subsystems/Intake.cpp b/wpilibcExamples/src/main/cpp/examples/UnitTest/cpp/subsystems/Intake.cpp new file mode 100644 index 0000000000..5a878e75f3 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/UnitTest/cpp/subsystems/Intake.cpp @@ -0,0 +1,26 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "subsystems/Intake.h" + +void Intake::Deploy() { + m_piston.Set(frc::DoubleSolenoid::Value::kForward); +} + +void Intake::Retract() { + m_piston.Set(frc::DoubleSolenoid::Value::kReverse); + m_motor.Set(0); // turn off the motor +} + +void Intake::Activate(double speed) { + if (IsDeployed()) { + m_motor.Set(speed); + } else { // if piston isn't open, do nothing + m_motor.Set(0); + } +} + +bool Intake::IsDeployed() const { + return m_piston.Get() == frc::DoubleSolenoid::Value::kForward; +} diff --git a/wpilibcExamples/src/main/cpp/examples/UnitTest/include/Constants.h b/wpilibcExamples/src/main/cpp/examples/UnitTest/include/Constants.h new file mode 100644 index 0000000000..2ad9b079a4 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/UnitTest/include/Constants.h @@ -0,0 +1,26 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +/** + * The Constants header provides a convenient place for teams to hold robot-wide + * numerical or bool constants. This should not be used for any other purpose. + * + * It is generally a good idea to place constants into subsystem- or + * command-specific namespaces within this header, which can then be used where + * they are needed. + */ + +namespace IntakeConstants { +constexpr int kMotorPort = 1; + +constexpr int kPistonFwdChannel = 0; +constexpr int kPistonRevChannel = 1; +constexpr double kIntakeSpeed = 0.5; +} // namespace IntakeConstants + +namespace OperatorConstants { +constexpr int kJoystickIndex = 0; +} // namespace OperatorConstants diff --git a/wpilibcExamples/src/main/cpp/examples/UnitTest/include/Robot.h b/wpilibcExamples/src/main/cpp/examples/UnitTest/include/Robot.h new file mode 100644 index 0000000000..492dded5bf --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/UnitTest/include/Robot.h @@ -0,0 +1,20 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include +#include + +#include "Constants.h" +#include "subsystems/Intake.h" + +class Robot : public frc::TimedRobot { + public: + void TeleopPeriodic() override; + + private: + Intake m_intake; + frc::Joystick m_joystick{OperatorConstants::kJoystickIndex}; +}; diff --git a/wpilibcExamples/src/main/cpp/examples/UnitTest/include/subsystems/Intake.h b/wpilibcExamples/src/main/cpp/examples/UnitTest/include/subsystems/Intake.h new file mode 100644 index 0000000000..20cf1b6320 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/UnitTest/include/subsystems/Intake.h @@ -0,0 +1,24 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include +#include + +#include "Constants.h" + +class Intake { + public: + void Deploy(); + void Retract(); + void Activate(double speed); + bool IsDeployed() const; + + private: + frc::PWMSparkMax m_motor{IntakeConstants::kMotorPort}; + frc::DoubleSolenoid m_piston{frc::PneumaticsModuleType::CTREPCM, + IntakeConstants::kPistonFwdChannel, + IntakeConstants::kPistonRevChannel}; +}; diff --git a/wpilibcExamples/src/main/cpp/examples/examples.json b/wpilibcExamples/src/main/cpp/examples/examples.json index 61f189de99..499a2fb62d 100644 --- a/wpilibcExamples/src/main/cpp/examples/examples.json +++ b/wpilibcExamples/src/main/cpp/examples/examples.json @@ -622,7 +622,6 @@ ], "foldername": "StateSpaceFlywheel", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -641,7 +640,6 @@ ], "foldername": "StateSpaceFlywheelSysId", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -657,7 +655,6 @@ ], "foldername": "StateSpaceElevator", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -673,7 +670,6 @@ ], "foldername": "StateSpaceArm", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -690,7 +686,6 @@ ], "foldername": "ElevatorSimulation", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -706,7 +701,6 @@ ], "foldername": "DifferentialDrivePoseEstimator", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -722,7 +716,6 @@ ], "foldername": "MecanumDrivePoseEstimator", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -740,7 +733,16 @@ ], "foldername": "ArmSimulation", "gradlebase": "cpp", - "mainclass": "Main", + "commandversion": 2 + }, + { + "name": "UnitTesting", + "description": "Demonstrates basic unit testing for a robot project.", + "tags": [ + "Testing" + ], + "foldername": "UnitTest", + "gradlebase": "cpp", "commandversion": 2 }, { @@ -758,7 +760,6 @@ ], "foldername": "SimpleDifferentialDriveSimulation", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -776,7 +777,6 @@ ], "foldername": "StateSpaceDifferentialDriveSimulation", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 }, { @@ -793,7 +793,6 @@ ], "foldername": "SwerveDrivePoseEstimator", "gradlebase": "cpp", - "mainclass": "Main", "commandversion": 2 } ] diff --git a/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/main.cpp b/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/main.cpp new file mode 100644 index 0000000000..285c1d5267 --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/main.cpp @@ -0,0 +1,17 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include + +#include "gtest/gtest.h" + +/** + * Runs all unit tests. + */ +int main(int argc, char** argv) { + HAL_Initialize(500, 0); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/subsystems/IntakeTest.cpp b/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/subsystems/IntakeTest.cpp new file mode 100644 index 0000000000..ae1d786c3f --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/UnitTest/cpp/subsystems/IntakeTest.cpp @@ -0,0 +1,46 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include + +#include +#include +#include + +#include "Constants.h" +#include "subsystems/Intake.h" + +class IntakeTest : public testing::Test { + protected: + Intake intake; // create our intake + frc::sim::PWMSim simMotor{ + IntakeConstants::kMotorPort}; // create our simulation PWM + frc::sim::DoubleSolenoidSim simPiston{ + frc::PneumaticsModuleType::CTREPCM, IntakeConstants::kPistonFwdChannel, + IntakeConstants::kPistonRevChannel}; // create our simulation solenoid +}; + +TEST_F(IntakeTest, DoesntWorkWhenClosed) { + intake.Retract(); // close the intake + intake.Activate(0.5); // try to activate the motor + EXPECT_DOUBLE_EQ( + 0.0, + simMotor.GetSpeed()); // make sure that the value set to the motor is 0 +} + +TEST_F(IntakeTest, WorksWhenOpen) { + intake.Deploy(); + intake.Activate(0.5); + EXPECT_DOUBLE_EQ(0.5, simMotor.GetSpeed()); +} + +TEST_F(IntakeTest, Retract) { + intake.Retract(); + EXPECT_EQ(frc::DoubleSolenoid::Value::kReverse, simPiston.Get()); +} + +TEST_F(IntakeTest, Deploy) { + intake.Deploy(); + EXPECT_EQ(frc::DoubleSolenoid::Value::kForward, simPiston.Get()); +} diff --git a/wpilibjExamples/build.gradle b/wpilibjExamples/build.gradle index 45d56f2316..6422c59e82 100644 --- a/wpilibjExamples/build.gradle +++ b/wpilibjExamples/build.gradle @@ -174,8 +174,9 @@ model { new groovy.json.JsonSlurper().parseText(exampleFile.text).each { entry -> project.tasks.create("run${entry.foldername}", JavaExec) { run -> - mainClass = "edu.wpi.first.wpilibj.examples." + entry.foldername + ".Main" - classpath = sourceSets.main.runtimeClasspath + run.group "run examples" + run.mainClass = "edu.wpi.first.wpilibj.examples." + entry.foldername + "." + entry.mainclass + run.classpath = sourceSets.main.runtimeClasspath run.dependsOn it.tasks.install run.systemProperty 'java.library.path', filePath run.environment 'LD_LIBRARY_PATH', filePath @@ -186,6 +187,23 @@ model { run.jvmArgs = ['-XstartOnFirstThread'] } } + project.tasks.create("test${entry.foldername}", Test) { testTask -> + testTask.group "verification" + testTask.useJUnitPlatform() + testTask.filter { + includeTestsMatching("edu.wpi.first.wpilibj.examples.${entry.foldername}.*") + setFailOnNoMatchingTests(false) + } + testTask.classpath = sourceSets.test.runtimeClasspath + testTask.systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true' + testTask.testLogging { + events "failed" + exceptionFormat "full" + } + testTask.systemProperty 'java.library.path', filePath + testTask.environment 'LD_LIBRARY_PATH', filePath + testTask.workingDir filePath + } } found = true diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json index 9a03f82ecb..1b76a28a23 100644 --- a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/examples.json @@ -733,6 +733,17 @@ "mainclass": "Main", "commandversion": 2 }, + { + "name": "UnitTesting", + "description": "Demonstrates basic unit testing for a robot project.", + "tags": [ + "Testing" + ], + "foldername": "unittest", + "gradlebase": "java", + "mainclass": "Main", + "commandversion": 2 + }, { "name": "DifferentialDrivePoseEstimator", "description": "Demonstrates the use of the DifferentialDrivePoseEstimator as a replacement for differential drive odometry.", diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Constants.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Constants.java new file mode 100644 index 0000000000..cdcfb9d4f6 --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Constants.java @@ -0,0 +1,25 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.examples.unittest; + +/** + * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean + * constants. This class should not be used for any other purpose. All constants should be declared + * globally (i.e. public static). Do not put anything functional in this class. + * + *

It is advised to statically import this class (or one of its inner classes) wherever the + * constants are needed, to reduce verbosity. + */ +public final class Constants { + public static final int kJoystickIndex = 0; + + public static final class IntakeConstants { + public static final int kMotorPort = 1; + + public static final int kPistonFwdChannel = 0; + public static final int kPistonRevChannel = 1; + public static final double kIntakeSpeed = 0.5; + } +} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Main.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Main.java new file mode 100644 index 0000000000..4434f47a62 --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Main.java @@ -0,0 +1,25 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.examples.unittest; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Robot.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Robot.java new file mode 100644 index 0000000000..9f66c27e94 --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/Robot.java @@ -0,0 +1,41 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.examples.unittest; + +import edu.wpi.first.wpilibj.Joystick; +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.examples.unittest.Constants.IntakeConstants; +import edu.wpi.first.wpilibj.examples.unittest.subsystems.Intake; + +/** + * The VM is configured to automatically run this class, and to call the functions corresponding to + * each mode, as described in the TimedRobot documentation. If you change the name of this class or + * the package after creating this project, you must also update the build.gradle file in the + * project. + */ +public class Robot extends TimedRobot { + private Intake m_intake = new Intake(); + private Joystick m_joystick = new Joystick(Constants.kJoystickIndex); + + /** This function is called periodically during operator control. */ + @Override + public void teleopPeriodic() { + // Activate the intake while the trigger is held + if (m_joystick.getTrigger()) { + m_intake.activate(IntakeConstants.kIntakeSpeed); + } else { + m_intake.activate(0); + } + + // Toggle deploying the intake when the top button is pressed + if (m_joystick.getTop()) { + if (m_intake.isDeployed()) { + m_intake.retract(); + } else { + m_intake.deploy(); + } + } + } +} diff --git a/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/subsystems/Intake.java b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/subsystems/Intake.java new file mode 100644 index 0000000000..a51d505a98 --- /dev/null +++ b/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/unittest/subsystems/Intake.java @@ -0,0 +1,51 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.examples.unittest.subsystems; + +import edu.wpi.first.wpilibj.DoubleSolenoid; +import edu.wpi.first.wpilibj.PneumaticsModuleType; +import edu.wpi.first.wpilibj.examples.unittest.Constants.IntakeConstants; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; + +public class Intake implements AutoCloseable { + private final PWMSparkMax m_motor; + private final DoubleSolenoid m_piston; + + public Intake() { + m_motor = new PWMSparkMax(IntakeConstants.kMotorPort); + m_piston = + new DoubleSolenoid( + PneumaticsModuleType.CTREPCM, + IntakeConstants.kPistonFwdChannel, + IntakeConstants.kPistonRevChannel); + } + + public void deploy() { + m_piston.set(DoubleSolenoid.Value.kForward); + } + + public void retract() { + m_piston.set(DoubleSolenoid.Value.kReverse); + m_motor.set(0); // turn off the motor + } + + public void activate(double speed) { + if (isDeployed()) { + m_motor.set(speed); + } else { // if piston isn't open, do nothing + m_motor.set(0); + } + } + + public boolean isDeployed() { + return m_piston.get() == DoubleSolenoid.Value.kForward; + } + + @Override + public void close() throws Exception { + m_piston.close(); + m_motor.close(); + } +} diff --git a/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/unittest/subsystems/IntakeTest.java b/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/unittest/subsystems/IntakeTest.java new file mode 100644 index 0000000000..33be835ef8 --- /dev/null +++ b/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/unittest/subsystems/IntakeTest.java @@ -0,0 +1,70 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.examples.unittest.subsystems; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.DoubleSolenoid; +import edu.wpi.first.wpilibj.PneumaticsModuleType; +import edu.wpi.first.wpilibj.examples.unittest.Constants.IntakeConstants; +import edu.wpi.first.wpilibj.simulation.DoubleSolenoidSim; +import edu.wpi.first.wpilibj.simulation.PWMSim; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class IntakeTest { + static final double DELTA = 1e-2; // acceptable deviation range + Intake m_intake; + PWMSim m_simMotor; + DoubleSolenoidSim m_simPiston; + + @BeforeEach // this method will run before each test + void setup() { + assert HAL.initialize(500, 0); // initialize the HAL, crash if failed + m_intake = new Intake(); // create our intake + m_simMotor = + new PWMSim(IntakeConstants.kMotorPort); // create our simulation PWM motor controller + m_simPiston = + new DoubleSolenoidSim( + PneumaticsModuleType.CTREPCM, + IntakeConstants.kPistonFwdChannel, + IntakeConstants.kPistonRevChannel); // create our simulation solenoid + } + + @SuppressWarnings("PMD.SignatureDeclareThrowsException") + @AfterEach // this method will run after each test + void shutdown() throws Exception { + m_intake.close(); // destroy our intake object + } + + @Test // marks this method as a test + void doesntWorkWhenClosed() { + m_intake.retract(); // close the intake + m_intake.activate(0.5); // try to activate the motor + assertEquals( + 0.0, m_simMotor.getSpeed(), DELTA); // make sure that the value set to the motor is 0 + } + + @Test + void worksWhenOpen() { + m_intake.deploy(); + m_intake.activate(0.5); + assertEquals(0.5, m_simMotor.getSpeed(), DELTA); + } + + @Test + void retractTest() { + m_intake.retract(); + assertEquals(DoubleSolenoid.Value.kReverse, m_simPiston.get()); + } + + @Test + void deployTest() { + m_intake.deploy(); + assertEquals(DoubleSolenoid.Value.kForward, m_simPiston.get()); + } +}