Files
allwpilib/commandsv3/src/test/java/org/wpilib/commands3/SchedulerTest.java

162 lines
5.0 KiB
Java
Raw Normal View History

// 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 org.wpilib.commands3;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
class SchedulerTest extends CommandTestBase {
@Test
void basic() {
var enabled = new AtomicBoolean(false);
var ran = new AtomicBoolean(false);
var command =
Command.noRequirements()
.executing(
coroutine -> {
do {
coroutine.yield();
} while (!enabled.get());
ran.set(true);
})
.named("Basic Command");
m_scheduler.schedule(command);
m_scheduler.run();
assertTrue(m_scheduler.isRunning(command), "Command should be running after being scheduled");
enabled.set(true);
m_scheduler.run();
if (m_scheduler.isRunning(command)) {
fail("Command should no longer be running after awaiting its completion");
}
assertTrue(ran.get());
}
@Test
@SuppressWarnings("PMD.ImmutableField") // PMD bugs
void atomicity() {
var mechanism =
new Mechanism("X", m_scheduler) {
int m_x = 0;
};
// Launch 100 commands that each call `x++` 500 times.
// If commands run on different threads, the lack of atomic
// operations or locks will mean the final number will be
// less than the expected 50,000
int numCommands = 100;
int iterations = 500;
for (int cmdCount = 0; cmdCount < numCommands; cmdCount++) {
var command =
Command.noRequirements()
.executing(
coroutine -> {
for (int i = 0; i < iterations; i++) {
mechanism.m_x++;
coroutine.yield();
}
})
.named("CountCommand[" + cmdCount + "]");
m_scheduler.schedule(command);
}
for (int i = 0; i < iterations; i++) {
m_scheduler.run();
}
assertEquals(numCommands * iterations, mechanism.m_x);
}
@Test
@SuppressWarnings("PMD.ImmutableField") // PMD bugs
void runMechanism() {
var example =
new Mechanism("Counting", m_scheduler) {
int m_x = 0;
};
Command countToTen =
example
.run(
coroutine -> {
example.m_x = 0;
for (int i = 0; i < 10; i++) {
coroutine.yield();
example.m_x++;
}
})
.named("Count To Ten");
m_scheduler.schedule(countToTen);
for (int i = 0; i < 10; i++) {
m_scheduler.run();
}
m_scheduler.run();
assertEquals(10, example.m_x);
}
@Test
void compositionsDoNotNeedRequirements() {
var m1 = new Mechanism("M1", m_scheduler);
var m2 = new Mechanism("m2", m_scheduler);
// the group has no requirements, but can schedule child commands that do
var group =
Command.noRequirements()
.executing(
co -> {
co.awaitAll(
m1.run(Coroutine::park).named("M1 Command"),
m2.run(Coroutine::park).named("M2 Command"));
})
.named("Composition");
m_scheduler.schedule(group);
m_scheduler.run(); // start m1 and m2 commands
assertEquals(3, m_scheduler.getRunningCommands().size());
}
@Test
void nestedMechanisms() {
var superstructure =
new Mechanism("Superstructure", m_scheduler) {
private final Mechanism m_elevator = new Mechanism("Elevator", m_scheduler);
private final Mechanism m_arm = new Mechanism("Arm", m_scheduler);
public Command superCommand() {
return run(co -> {
co.await(m_elevator.run(Coroutine::park).named("Elevator Subcommand"));
co.await(m_arm.run(Coroutine::park).named("Arm Subcommand"));
})
.named("Super Command");
}
};
m_scheduler.schedule(superstructure.superCommand());
m_scheduler.run();
assertEquals(
List.of(superstructure.m_arm.getDefaultCommand()),
superstructure.m_arm.getRunningCommands(),
"Arm should only be running its default command");
// Scheduling something that requires an in-use inner mechanism cancels the outer command
m_scheduler.schedule(superstructure.m_elevator.run(Coroutine::park).named("Conflict"));
m_scheduler.run(); // schedules the default superstructure command
m_scheduler.run(); // starts running the default superstructure command
assertEquals(List.of(superstructure.getDefaultCommand()), superstructure.getRunningCommands());
}
}