Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion testing/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies {
nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop)
simulationRelease wpi.sim.enableRelease()

testImplementation('org.junit.platform:junit-platform-testkit')
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
}

Expand All @@ -37,7 +38,9 @@ wpi.java.configureTestTasks(test)

tasks.named('test') {
// Support running both JUnit Vintage and JUnit Jupiter tests
useJUnitPlatform()
useJUnitPlatform {
excludeTags('ignore-outside-testkit')
}
systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ public final class WPILibExtension
@Override
public void beforeAll(ExtensionContext context) {
// See https://www.chiefdelphi.com/t/driverstation-getalliance-in-gradle-test/
HAL.initialize(500, 0);
if (!HAL.initialize(500, 0)) {
throw new IllegalStateException("Could not initialize Hardware Abstraction Layer");
}
DriverStationSim.setEnabled(true);
DriverStationSim.notifyNewData();
CommandScheduler.getInstance().enable();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package com.team2813.lib2813.testing.junit.jupiter;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;

import edu.wpi.first.hal.HAL;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.RobotState;
import edu.wpi.first.wpilibj.simulation.DriverStationSim;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.CommandScheduler;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.testkit.engine.EngineExecutionResults;
import org.junit.platform.testkit.engine.EngineTestKit;
import org.junit.platform.testkit.engine.Events;

/** Tests for {@link WPILibExtension}. */
public class WPILibExtensionTest {

static final Command FAKE_COMMAND = new Command() {};

@BeforeAll
static void initializeHal() {
if (!HAL.initialize(500, 0)) {
throw new IllegalStateException("Could not initialize Hardware Abstraction Layer");
}
}

@ExtendWith(WPILibExtension.class)
@Tag("ignore-outside-testkit")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public static class SampleTest {

@BeforeAll
public static void verifyFakeCommandNotScheduledBeforeAll() {
CommandScheduler commandScheduler = CommandScheduler.getInstance();
assertWithMessage("Expect all commands to have been cancelled")
.that(commandScheduler.isScheduled(FAKE_COMMAND))
.isFalse();
}

@BeforeAll
public static void verifyDriverStationEnabled() {
assertThat(DriverStation.isEnabled()).isTrue();
}

@Test
@Order(1)
public void verifyFakeCommandNotScheduledBeforeTest() {
CommandScheduler commandScheduler = CommandScheduler.getInstance();
assertWithMessage("Expect all commands to have been cancelled")
.that(commandScheduler.isScheduled(FAKE_COMMAND))
.isFalse();

commandScheduler.schedule(FAKE_COMMAND);
assertThat(commandScheduler.isScheduled(FAKE_COMMAND));
}

@Test
@Order(2)
public void verifyFakeCommandNotScheduledAfterTest(CommandTester commandTester) {
CommandScheduler commandScheduler = CommandScheduler.getInstance();
assertWithMessage("Expect all commands to have been cancelled")
.that(commandScheduler.isScheduled(FAKE_COMMAND))
.isFalse();

commandScheduler.schedule(FAKE_COMMAND);
assertThat(commandScheduler.isScheduled(FAKE_COMMAND));
}

@Test
@Order(3)
public void verifyCommandTester(CommandTester commandTester) {
VerifiableCommand command = new VerifiableCommand();
commandTester.runUntilComplete(command);
command.verify();
}

@AfterAll
public static void verifyFakeCommandNotScheduledAfterAllTests() {
CommandScheduler commandScheduler = CommandScheduler.getInstance();
assertWithMessage("Expect all commands to have been cancelled")
.that(commandScheduler.isScheduled(FAKE_COMMAND))
.isFalse();
}
} // end SampleTest

@Test
void verifyExtension() {
// Arrange
withDriverStationTemporarilyEnabled(
() -> {
// Schedule FAKE_COMMAND
CommandScheduler commandScheduler = CommandScheduler.getInstance();
commandScheduler.enable();
commandScheduler.schedule(FAKE_COMMAND);
boolean isScheduled = commandScheduler.isScheduled(FAKE_COMMAND);
commandScheduler.disable();
assertThat(isScheduled).isTrue();
});

// Act
EngineExecutionResults results =
EngineTestKit.engine("junit-jupiter").selectors(selectClass(SampleTest.class)).execute();

// Assert
assertHasNoFailures(results);
}

private void withDriverStationTemporarilyEnabled(Runnable runnable) {
assertThat(RobotState.isDisabled()).isTrue();
DriverStationSim.setEnabled(true);
DriverStationSim.notifyNewData();
assertThat(RobotState.isDisabled()).isFalse();

try {
runnable.run();
} finally {
DriverStationSim.setEnabled(false);
DriverStationSim.notifyNewData();
}
}

private void assertHasNoFailures(EngineExecutionResults results) {
assertHasNoFailures(results.containerEvents());
assertHasNoFailures(results.testEvents());
}

private void assertHasNoFailures(Events events) {
events.assertStatistics(
stats -> {
stats.skipped(0);
stats.failed(0);
});
}

private static class VerifiableCommand extends Command {
private static final int EXPECTED_EXECUTION_COUNT = 4;
private int initializedCount = 0;
private int executionCount = 0;

void verify() {
assertWithMessage("initialize() should be called").that(initializedCount).isGreaterThan(0);
assertWithMessage("initialize() should not be called more than once")
.that(initializedCount)
.isLessThan(2);
assertWithMessage("execute() should be called until isFinished() returns false")
.that(executionCount)
.isEqualTo(EXPECTED_EXECUTION_COUNT);
}

@Override
public void initialize() {
initializedCount++;
}

@Override
public void execute() {
executionCount++;
}

@Override
public boolean isFinished() {
return executionCount >= EXPECTED_EXECUTION_COUNT;
}
}
}
Loading