diff --git a/testing/build.gradle b/testing/build.gradle index bb4a224b..afb273a6 100644 --- a/testing/build.gradle +++ b/testing/build.gradle @@ -8,12 +8,18 @@ version = '1.0.0-alpha.1' repositories { // Use Maven Central for resolving dependencies. mavenCentral() + maven { + // WPI + url 'https://frcmaven.wpi.edu/artifactory/release/' + } } dependencies { implementation wpi.java.deps.wpilib() implementation wpi.java.vendor.java() 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) @@ -22,8 +28,7 @@ dependencies { nativeRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.desktop) nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop) simulationRelease wpi.sim.enableRelease() - testImplementation(platform('org.junit:junit-bom:5.13.1')) - testImplementation('org.junit.jupiter:junit-jupiter') + testRuntimeOnly('org.junit.platform:junit-platform-launcher') } @@ -45,15 +50,6 @@ javadoc { ] } -// Generates Javadocs for test libraries under /build/docs/testjavadoc -// https://discuss.gradle.org/t/how-configure-gradle-to-generate-javadoc-for-testing-classes-junit/12764/6 -task testJavadoc(type: Javadoc) { - source = sourceSets.test.allJava - classpath = sourceSets.test.compileClasspath - destinationDir = file("${buildDir}/docs/testjavadoc") - println (title == null ? '' : title) -} - task sourceJar(type: Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' diff --git a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/CommandTester.java b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/CommandTester.java new file mode 100644 index 00000000..42bc7741 --- /dev/null +++ b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/CommandTester.java @@ -0,0 +1,14 @@ +package com.team2813.lib2813.testing.junit.jupiter; + +import edu.wpi.first.wpilibj2.command.Command; + +/** + * Allows tests to run commands. + * + *

Tests can get an instance by using {@link WPILibExtension}. + */ +public interface CommandTester { + + /** Schedules the provided command and runs it until it completes. */ + void runUntilComplete(Command command); +} diff --git a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtension.java b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtension.java new file mode 100644 index 00000000..d6787beb --- /dev/null +++ b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtension.java @@ -0,0 +1,96 @@ +package com.team2813.lib2813.testing.junit.jupiter; + +import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.simulation.DriverStationSim; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; + +/** + * JUnit Jupiter extension for testing code that depends on WPILib. + * + *

Also provides a {@link CommandTester} for tests. + * + *

Example use: + * + *

{@code
+ * @ExtendWith(WPILibExtension.class)
+ * public final class FlightSubsystemTest {
+ *
+ *   @Test
+ *   public void initiallyNotInAir() {
+ *     var flight = new FlightSubsystem();
+ *
+ *     assertThat(flight.inAir()).isFalse();
+ *   }
+ *
+ *   @Test
+ *   public void takesFlight(CommandTester commandTester) {
+ *     var flight = new FlightSubsystem();
+ *     Command takeOff = flight.createTakeOffCommandCommand();
+ *
+ *     commandTester.runUntilComplete(takeOff);
+ *
+ *     assertThat(flight.inAir()).isTrue();
+ *   }
+ * }
+ * }
+ */ +public final class WPILibExtension + implements Extension, + AfterAllCallback, + AfterEachCallback, + BeforeAllCallback, + ParameterResolver { + + @Override + public void beforeAll(ExtensionContext context) { + // See https://www.chiefdelphi.com/t/driverstation-getalliance-in-gradle-test/ + HAL.initialize(500, 0); + DriverStationSim.setEnabled(true); + DriverStationSim.notifyNewData(); + CommandScheduler.getInstance().enable(); + CommandScheduler.getInstance().cancelAll(); + CommandScheduler.getInstance().unregisterAllSubsystems(); + } + + @Override + public void afterEach(ExtensionContext context) { + CommandScheduler.getInstance().cancelAll(); + CommandScheduler.getInstance().unregisterAllSubsystems(); + } + + @Override + public void afterAll(ExtensionContext context) { + CommandScheduler.getInstance().cancelAll(); + CommandScheduler.getInstance().unregisterAllSubsystems(); + CommandScheduler.getInstance().disable(); + DriverStationSim.setEnabled(false); + DriverStationSim.notifyNewData(); + } + + @Override + public boolean supportsParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) + throws ParameterResolutionException { + return CommandTester.class.equals(parameterContext.getParameter().getType()); + } + + @Override + public CommandTester resolveParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) { + return command -> { + CommandScheduler scheduler = CommandScheduler.getInstance(); + command.schedule(); + do { + scheduler.run(); + } while (scheduler.isScheduled(command)); + }; + } +} diff --git a/testing/vendordeps/WPILibNewCommands.json b/testing/vendordeps/WPILibNewCommands.json new file mode 100644 index 00000000..b7aedf69 --- /dev/null +++ b/testing/vendordeps/WPILibNewCommands.json @@ -0,0 +1,38 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2025", + "mavenUrls": [], + "jsonUrl": "", + "javaDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-java", + "version": "wpilib" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-cpp", + "version": "wpilib", + "libName": "wpilibNewCommands", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} \ No newline at end of file