[commands] Add property tests for command compositions (#4715)

This commit is contained in:
Starlight220
2022-11-28 02:23:56 +02:00
committed by GitHub
parent e4ac09077c
commit 8958b2a4da
31 changed files with 579 additions and 59 deletions

View File

@@ -64,6 +64,10 @@
<Bug pattern="NS_DANGEROUS_NON_SHORT_CIRCUIT" />
<Source name="SequentialCommandGroup.java" />
</Match>
<Match>
<Bug pattern="NS_DANGEROUS_NON_SHORT_CIRCUIT" />
<Source name="SelectCommand.java" />
</Match>
<Match>
<Bug pattern="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" />
<Source name="AntJunitLauncher.java" />

View File

@@ -19,6 +19,7 @@ public class ParallelCommandGroup extends CommandGroupBase {
// maps commands in this group to whether they are still running
private final Map<Command, Boolean> m_commands = new HashMap<>();
private boolean m_runWhenDisabled = true;
private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
/**
* Creates a new ParallelCommandGroup. The given commands will be executed simultaneously. The
@@ -50,6 +51,9 @@ public class ParallelCommandGroup extends CommandGroupBase {
m_commands.put(command, false);
m_requirements.addAll(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
m_interruptBehavior = InterruptionBehavior.kCancelSelf;
}
}
}
@@ -95,4 +99,9 @@ public class ParallelCommandGroup extends CommandGroupBase {
public boolean runsWhenDisabled() {
return m_runWhenDisabled;
}
@Override
public InterruptionBehavior getInterruptionBehavior() {
return m_interruptBehavior;
}
}

View File

@@ -22,6 +22,7 @@ public class ParallelDeadlineGroup extends CommandGroupBase {
private boolean m_runWhenDisabled = true;
private boolean m_finished = true;
private Command m_deadline;
private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
/**
* Creates a new ParallelDeadlineGroup. The given commands (including the deadline) will be
@@ -72,6 +73,9 @@ public class ParallelDeadlineGroup extends CommandGroupBase {
m_commands.put(command, false);
m_requirements.addAll(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
m_interruptBehavior = InterruptionBehavior.kCancelSelf;
}
}
}
@@ -119,4 +123,9 @@ public class ParallelDeadlineGroup extends CommandGroupBase {
public boolean runsWhenDisabled() {
return m_runWhenDisabled;
}
@Override
public InterruptionBehavior getInterruptionBehavior() {
return m_interruptBehavior;
}
}

View File

@@ -20,6 +20,7 @@ public class ParallelRaceGroup extends CommandGroupBase {
private final Set<Command> m_commands = new HashSet<>();
private boolean m_runWhenDisabled = true;
private boolean m_finished = true;
private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
/**
* Creates a new ParallelCommandRace. The given commands will be executed simultaneously, and will
@@ -51,6 +52,9 @@ public class ParallelRaceGroup extends CommandGroupBase {
m_commands.add(command);
m_requirements.addAll(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
m_interruptBehavior = InterruptionBehavior.kCancelSelf;
}
}
}
@@ -88,4 +92,9 @@ public class ParallelRaceGroup extends CommandGroupBase {
public boolean runsWhenDisabled() {
return m_runWhenDisabled;
}
@Override
public InterruptionBehavior getInterruptionBehavior() {
return m_interruptBehavior;
}
}

View File

@@ -31,6 +31,8 @@ public class SelectCommand extends CommandBase {
private final Supplier<Object> m_selector;
private final Supplier<Command> m_toRun;
private Command m_selectedCommand;
private boolean m_runsWhenDisabled = true;
private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
/**
* Creates a new selectcommand.
@@ -50,6 +52,10 @@ public class SelectCommand extends CommandBase {
for (Command command : m_commands.values()) {
m_requirements.addAll(command.getRequirements());
m_runsWhenDisabled &= command.runsWhenDisabled();
if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
m_interruptBehavior = InterruptionBehavior.kCancelSelf;
}
}
}
@@ -62,6 +68,10 @@ public class SelectCommand extends CommandBase {
m_commands = null;
m_selector = null;
m_toRun = requireNonNullParam(toRun, "toRun", "SelectCommand");
// we have no way of checking the underlying command, so default.
m_runsWhenDisabled = false;
m_interruptBehavior = InterruptionBehavior.kCancelSelf;
}
@Override
@@ -97,14 +107,11 @@ public class SelectCommand extends CommandBase {
@Override
public boolean runsWhenDisabled() {
if (m_commands != null) {
boolean runsWhenDisabled = true;
for (Command command : m_commands.values()) {
runsWhenDisabled &= command.runsWhenDisabled();
}
return runsWhenDisabled;
} else {
return m_toRun.get().runsWhenDisabled();
}
return m_runsWhenDisabled;
}
@Override
public InterruptionBehavior getInterruptionBehavior() {
return m_interruptBehavior;
}
}

View File

@@ -18,6 +18,7 @@ public class SequentialCommandGroup extends CommandGroupBase {
private final List<Command> m_commands = new ArrayList<>();
private int m_currentCommandIndex = -1;
private boolean m_runWhenDisabled = true;
private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
/**
* Creates a new SequentialCommandGroup. The given commands will be run sequentially, with the
@@ -44,6 +45,9 @@ public class SequentialCommandGroup extends CommandGroupBase {
m_commands.add(command);
m_requirements.addAll(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
m_interruptBehavior = InterruptionBehavior.kCancelSelf;
}
}
}
@@ -94,4 +98,9 @@ public class SequentialCommandGroup extends CommandGroupBase {
public boolean runsWhenDisabled() {
return m_runWhenDisabled;
}
@Override
public InterruptionBehavior getInterruptionBehavior() {
return m_interruptBehavior;
}
}

View File

@@ -56,6 +56,11 @@ bool ParallelCommandGroup::RunsWhenDisabled() const {
return m_runWhenDisabled;
}
Command::InterruptionBehavior ParallelCommandGroup::GetInterruptionBehavior()
const {
return m_interruptBehavior;
}
void ParallelCommandGroup::AddCommands(
std::vector<std::unique_ptr<Command>>&& commands) {
for (auto&& command : commands) {
@@ -75,6 +80,10 @@ void ParallelCommandGroup::AddCommands(
command->SetGrouped(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command), false);
} else {
throw FRC_MakeError(frc::err::CommandIllegalUse,

View File

@@ -53,6 +53,11 @@ bool ParallelDeadlineGroup::RunsWhenDisabled() const {
return m_runWhenDisabled;
}
Command::InterruptionBehavior ParallelDeadlineGroup::GetInterruptionBehavior()
const {
return m_interruptBehavior;
}
void ParallelDeadlineGroup::AddCommands(
std::vector<std::unique_ptr<Command>>&& commands) {
if (!RequireUngrouped(commands)) {
@@ -70,6 +75,10 @@ void ParallelDeadlineGroup::AddCommands(
command->SetGrouped(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command), false);
} else {
throw FRC_MakeError(frc::err::CommandIllegalUse,

View File

@@ -43,6 +43,11 @@ bool ParallelRaceGroup::RunsWhenDisabled() const {
return m_runWhenDisabled;
}
Command::InterruptionBehavior ParallelRaceGroup::GetInterruptionBehavior()
const {
return m_interruptBehavior;
}
void ParallelRaceGroup::AddCommands(
std::vector<std::unique_ptr<Command>>&& commands) {
if (!RequireUngrouped(commands)) {
@@ -60,6 +65,10 @@ void ParallelRaceGroup::AddCommands(
command->SetGrouped(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command));
} else {
throw FRC_MakeError(frc::err::CommandIllegalUse,

View File

@@ -53,6 +53,11 @@ bool SequentialCommandGroup::RunsWhenDisabled() const {
return m_runWhenDisabled;
}
Command::InterruptionBehavior SequentialCommandGroup::GetInterruptionBehavior()
const {
return m_interruptBehavior;
}
void SequentialCommandGroup::AddCommands(
std::vector<std::unique_ptr<Command>>&& commands) {
if (!RequireUngrouped(commands)) {
@@ -69,6 +74,10 @@ void SequentialCommandGroup::AddCommands(
command->SetGrouped(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command));
}
}

View File

@@ -84,11 +84,15 @@ class ParallelCommandGroup
bool RunsWhenDisabled() const override;
Command::InterruptionBehavior GetInterruptionBehavior() const override;
private:
void AddCommands(std::vector<std::unique_ptr<Command>>&& commands) final;
std::vector<std::pair<std::unique_ptr<Command>, bool>> m_commands;
bool m_runWhenDisabled{true};
Command::InterruptionBehavior m_interruptBehavior{
Command::InterruptionBehavior::kCancelIncoming};
bool isRunning = false;
};
} // namespace frc2

View File

@@ -92,6 +92,8 @@ class ParallelDeadlineGroup
bool RunsWhenDisabled() const override;
Command::InterruptionBehavior GetInterruptionBehavior() const override;
private:
void AddCommands(std::vector<std::unique_ptr<Command>>&& commands) final;
@@ -100,6 +102,8 @@ class ParallelDeadlineGroup
std::vector<std::pair<std::unique_ptr<Command>, bool>> m_commands;
Command* m_deadline;
bool m_runWhenDisabled{true};
Command::InterruptionBehavior m_interruptBehavior{
Command::InterruptionBehavior::kCancelIncoming};
bool m_finished{true};
};
} // namespace frc2

View File

@@ -72,11 +72,15 @@ class ParallelRaceGroup
bool RunsWhenDisabled() const override;
Command::InterruptionBehavior GetInterruptionBehavior() const override;
private:
void AddCommands(std::vector<std::unique_ptr<Command>>&& commands) final;
std::vector<std::unique_ptr<Command>> m_commands;
bool m_runWhenDisabled{true};
Command::InterruptionBehavior m_interruptBehavior{
Command::InterruptionBehavior::kCancelIncoming};
bool m_finished{false};
bool isRunning = false;
};

View File

@@ -69,6 +69,10 @@ class SelectCommand : public CommandHelper<CommandBase, SelectCommand<Key>> {
for (auto&& command : foo) {
this->AddRequirements(command.second->GetRequirements());
m_runsWhenDisabled &= command.second->RunsWhenDisabled();
if (command.second->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace(std::move(command.first), std::move(command.second));
}
}
@@ -86,6 +90,10 @@ class SelectCommand : public CommandHelper<CommandBase, SelectCommand<Key>> {
for (auto&& command : commands) {
this->AddRequirements(command.second->GetRequirements());
m_runsWhenDisabled &= command.second->RunsWhenDisabled();
if (command.second->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace(std::move(command.first), std::move(command.second));
}
}
@@ -118,6 +126,10 @@ class SelectCommand : public CommandHelper<CommandBase, SelectCommand<Key>> {
bool RunsWhenDisabled() const override { return m_runsWhenDisabled; }
Command::InterruptionBehavior GetInterruptionBehavior() const override {
return m_interruptBehavior;
}
protected:
std::unique_ptr<Command> TransferOwnership() && override {
return std::make_unique<SelectCommand>(std::move(*this));
@@ -129,6 +141,8 @@ class SelectCommand : public CommandHelper<CommandBase, SelectCommand<Key>> {
std::function<Command*()> m_toRun;
Command* m_selectedCommand;
bool m_runsWhenDisabled = true;
Command::InterruptionBehavior m_interruptBehavior{
Command::InterruptionBehavior::kCancelIncoming};
};
template <typename T>

View File

@@ -87,12 +87,16 @@ class SequentialCommandGroup
bool RunsWhenDisabled() const override;
Command::InterruptionBehavior GetInterruptionBehavior() const override;
private:
void AddCommands(std::vector<std::unique_ptr<Command>>&& commands) final;
wpi::SmallVector<std::unique_ptr<Command>, 4> m_commands;
size_t m_currentCommandIndex{invalid_index};
bool m_runWhenDisabled{true};
Command::InterruptionBehavior m_interruptBehavior{
Command::InterruptionBehavior::kCancelIncoming};
};
} // namespace frc2

View File

@@ -9,6 +9,7 @@ import static org.mockito.Mockito.when;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.simulation.DriverStationSim;
import edu.wpi.first.wpilibj2.command.Command.InterruptionBehavior;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
@@ -47,6 +48,7 @@ public class CommandTestBase {
when(m_mockCommand.getRequirements()).thenReturn(Set.of(requirements));
when(m_mockCommand.isFinished()).thenReturn(false);
when(m_mockCommand.runsWhenDisabled()).thenReturn(runWhenDisabled);
when(m_mockCommand.getInterruptionBehavior()).thenReturn(InterruptionBehavior.kCancelSelf);
}
public Command getMock() {

View File

@@ -0,0 +1,115 @@
// 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.wpilibj2.command;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import edu.wpi.first.wpilibj2.command.Command.InterruptionBehavior;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
interface MultiCompositionTestBase<T extends Command> extends SingleCompositionTestBase<T> {
T compose(Command... members);
@Override
default T composeSingle(Command member) {
return compose(member);
}
static Stream<Arguments> interruptible() {
return Stream.of(
arguments(
"AllCancelSelf",
InterruptionBehavior.kCancelSelf,
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelSelf),
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelSelf),
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelSelf)),
arguments(
"AllCancelIncoming",
InterruptionBehavior.kCancelIncoming,
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelIncoming),
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelIncoming),
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelIncoming)),
arguments(
"TwoCancelSelfOneIncoming",
InterruptionBehavior.kCancelSelf,
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelSelf),
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelSelf),
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelIncoming)),
arguments(
"TwoCancelIncomingOneSelf",
InterruptionBehavior.kCancelSelf,
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelIncoming),
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelIncoming),
new WaitUntilCommand(() -> false)
.withInterruptBehavior(InterruptionBehavior.kCancelSelf)));
}
@MethodSource
@ParameterizedTest(name = "interruptible[{index}]: {0}")
default void interruptible(
@SuppressWarnings("unused") String name,
InterruptionBehavior expected,
Command command1,
Command command2,
Command command3) {
var command = compose(command1, command2, command3);
assertEquals(expected, command.getInterruptionBehavior());
}
static Stream<Arguments> runsWhenDisabled() {
return Stream.of(
arguments(
"AllFalse",
false,
new WaitUntilCommand(() -> false).ignoringDisable(false),
new WaitUntilCommand(() -> false).ignoringDisable(false),
new WaitUntilCommand(() -> false).ignoringDisable(false)),
arguments(
"AllTrue",
true,
new WaitUntilCommand(() -> false).ignoringDisable(true),
new WaitUntilCommand(() -> false).ignoringDisable(true),
new WaitUntilCommand(() -> false).ignoringDisable(true)),
arguments(
"TwoTrueOneFalse",
false,
new WaitUntilCommand(() -> false).ignoringDisable(true),
new WaitUntilCommand(() -> false).ignoringDisable(true),
new WaitUntilCommand(() -> false).ignoringDisable(false)),
arguments(
"TwoFalseOneTrue",
false,
new WaitUntilCommand(() -> false).ignoringDisable(false),
new WaitUntilCommand(() -> false).ignoringDisable(false),
new WaitUntilCommand(() -> false).ignoringDisable(true)));
}
@MethodSource
@ParameterizedTest(name = "runsWhenDisabled[{index}]: {0}")
default void runsWhenDisabled(
@SuppressWarnings("unused") String name,
boolean expected,
Command command1,
Command command2,
Command command3) {
var command = compose(command1, command2, command3);
assertEquals(expected, command.runsWhenDisabled());
}
}

View File

@@ -14,7 +14,8 @@ import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.Test;
class ParallelCommandGroupTest extends CommandTestBase {
class ParallelCommandGroupTest extends CommandTestBase
implements MultiCompositionTestBase<ParallelCommandGroup> {
@Test
void parallelGroupScheduleTest() {
try (CommandScheduler scheduler = new CommandScheduler()) {
@@ -126,4 +127,9 @@ class ParallelCommandGroupTest extends CommandTestBase {
assertThrows(
IllegalArgumentException.class, () -> new ParallelCommandGroup(command1, command2));
}
@Override
public ParallelCommandGroup compose(Command... members) {
return new ParallelCommandGroup(members);
}
}

View File

@@ -11,9 +11,11 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
class ParallelDeadlineGroupTest extends CommandTestBase {
class ParallelDeadlineGroupTest extends CommandTestBase
implements MultiCompositionTestBase<ParallelDeadlineGroup> {
@Test
void parallelDeadlineScheduleTest() {
try (CommandScheduler scheduler = new CommandScheduler()) {
@@ -122,4 +124,9 @@ class ParallelDeadlineGroupTest extends CommandTestBase {
assertThrows(
IllegalArgumentException.class, () -> new ParallelDeadlineGroup(command1, command2));
}
@Override
public ParallelDeadlineGroup compose(Command... members) {
return new ParallelDeadlineGroup(members[0], Arrays.copyOfRange(members, 1, members.length));
}
}

View File

@@ -16,7 +16,8 @@ import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.Test;
class ParallelRaceGroupTest extends CommandTestBase {
class ParallelRaceGroupTest extends CommandTestBase
implements MultiCompositionTestBase<ParallelRaceGroup> {
@Test
void parallelRaceScheduleTest() {
try (CommandScheduler scheduler = new CommandScheduler()) {
@@ -202,4 +203,9 @@ class ParallelRaceGroupTest extends CommandTestBase {
assertFalse(scheduler.isScheduled(group));
}
}
@Override
public ParallelRaceGroup compose(Command... members) {
return new ParallelRaceGroup(members);
}
}

View File

@@ -9,11 +9,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;
class RepeatCommandTest extends CommandTestBase {
class RepeatCommandTest extends CommandTestBase
implements SingleCompositionTestBase<RepeatCommand> {
@Test
void callsMethodsCorrectly() {
var initCounter = new AtomicInteger(0);
@@ -66,18 +64,8 @@ class RepeatCommandTest extends CommandTestBase {
assertEquals(1, endCounter.get());
}
@EnumSource(Command.InterruptionBehavior.class)
@ParameterizedTest
void interruptible(Command.InterruptionBehavior interruptionBehavior) {
var command =
new WaitUntilCommand(() -> false).withInterruptBehavior(interruptionBehavior).repeatedly();
assertEquals(interruptionBehavior, command.getInterruptionBehavior());
}
@ValueSource(booleans = {true, false})
@ParameterizedTest
void runWhenDisabled(boolean runsWhenDisabled) {
var command = new WaitUntilCommand(() -> false).ignoringDisable(runsWhenDisabled).repeatedly();
assertEquals(runsWhenDisabled, command.runsWhenDisabled());
@Override
public RepeatCommand composeSingle(Command member) {
return member.repeatedly();
}
}

View File

@@ -9,10 +9,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
class SelectCommandTest extends CommandTestBase {
class SelectCommandTest extends CommandTestBase implements MultiCompositionTestBase<SelectCommand> {
@Test
void selectCommandTest() {
try (CommandScheduler scheduler = new CommandScheduler()) {
@@ -105,4 +106,13 @@ class SelectCommandTest extends CommandTestBase {
verify(command3, never()).end(true);
}
}
@Override
public SelectCommand compose(Command... members) {
var map = new HashMap<Object, Command>();
for (int i = 0; i < members.length; i++) {
map.put(i, members[i]);
}
return new SelectCommand(map, () -> 0);
}
}

View File

@@ -12,7 +12,8 @@ import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.Test;
class SequentialCommandGroupTest extends CommandTestBase {
class SequentialCommandGroupTest extends CommandTestBase
implements MultiCompositionTestBase<SequentialCommandGroup> {
@Test
void sequentialGroupScheduleTest() {
try (CommandScheduler scheduler = new CommandScheduler()) {
@@ -121,4 +122,9 @@ class SequentialCommandGroupTest extends CommandTestBase {
assertTrue(scheduler.isScheduled(command3));
}
}
@Override
public SequentialCommandGroup compose(Command... members) {
return new SequentialCommandGroup(members);
}
}

View File

@@ -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.
package edu.wpi.first.wpilibj2.command;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;
public interface SingleCompositionTestBase<T extends Command> {
T composeSingle(Command member);
@EnumSource(Command.InterruptionBehavior.class)
@ParameterizedTest
default void interruptible(Command.InterruptionBehavior interruptionBehavior) {
var command =
composeSingle(
new WaitUntilCommand(() -> false).withInterruptBehavior(interruptionBehavior));
assertEquals(interruptionBehavior, command.getInterruptionBehavior());
}
@ValueSource(booleans = {true, false})
@ParameterizedTest
default void runWhenDisabled(boolean runsWhenDisabled) {
var command =
composeSingle(new WaitUntilCommand(() -> false).ignoringDisable(runsWhenDisabled));
assertEquals(runsWhenDisabled, command.runsWhenDisabled());
}
}

View File

@@ -0,0 +1,204 @@
// 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 <memory>
#include <utility>
#include "CommandTestBase.h"
#include "frc2/command/Commands.h"
#include "gtest/gtest.h"
#include "make_vector.h"
namespace frc2 {
inline namespace single {
template <typename T>
class SingleCompositionRunsWhenDisabledTest : public CommandTestBase {};
TYPED_TEST_SUITE_P(SingleCompositionRunsWhenDisabledTest);
TYPED_TEST_P(SingleCompositionRunsWhenDisabledTest, True) {
auto param = true;
TypeParam command = TypeParam(
cmd::WaitUntil([] { return false; }).IgnoringDisable(param).Unwrap());
EXPECT_EQ(param, command.RunsWhenDisabled());
}
TYPED_TEST_P(SingleCompositionRunsWhenDisabledTest, False) {
auto param = false;
TypeParam command = TypeParam(
cmd::WaitUntil([] { return false; }).IgnoringDisable(param).Unwrap());
EXPECT_EQ(param, command.RunsWhenDisabled());
}
REGISTER_TYPED_TEST_SUITE_P(SingleCompositionRunsWhenDisabledTest, True, False);
template <typename T>
class SingleCompositionInterruptibilityTest : public CommandTestBase {};
TYPED_TEST_SUITE_P(SingleCompositionInterruptibilityTest);
TYPED_TEST_P(SingleCompositionInterruptibilityTest, CancelSelf) {
auto param = Command::InterruptionBehavior::kCancelSelf;
TypeParam command = TypeParam(cmd::WaitUntil([] { return false; })
.WithInterruptBehavior(param)
.Unwrap());
EXPECT_EQ(param, command.GetInterruptionBehavior());
}
TYPED_TEST_P(SingleCompositionInterruptibilityTest, CancelIncoming) {
auto param = Command::InterruptionBehavior::kCancelIncoming;
TypeParam command = TypeParam(cmd::WaitUntil([] { return false; })
.WithInterruptBehavior(param)
.Unwrap());
EXPECT_EQ(param, command.GetInterruptionBehavior());
}
REGISTER_TYPED_TEST_SUITE_P(SingleCompositionInterruptibilityTest, CancelSelf,
CancelIncoming);
#define INSTANTIATE_SINGLE_COMMAND_COMPOSITION_TEST_SUITE(Suite, \
CompositionType) \
INSTANTIATE_TYPED_TEST_SUITE_P(Suite, SingleCompositionInterruptibilityTest, \
CompositionType); \
INSTANTIATE_TYPED_TEST_SUITE_P(Suite, SingleCompositionRunsWhenDisabledTest, \
CompositionType);
} // namespace single
inline namespace multi {
template <typename T>
class MultiCompositionRunsWhenDisabledTest : public CommandTestBase {};
TYPED_TEST_SUITE_P(MultiCompositionRunsWhenDisabledTest);
TYPED_TEST_P(MultiCompositionRunsWhenDisabledTest, OneTrue) {
auto param = true;
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] { return false; }).IgnoringDisable(param))));
EXPECT_EQ(param, command.RunsWhenDisabled());
}
TYPED_TEST_P(MultiCompositionRunsWhenDisabledTest, OneFalse) {
auto param = false;
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] { return false; }).IgnoringDisable(param))));
EXPECT_EQ(param, command.RunsWhenDisabled());
}
TYPED_TEST_P(MultiCompositionRunsWhenDisabledTest, AllTrue) {
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] { return false; }).IgnoringDisable(true),
cmd::WaitUntil([] { return false; }).IgnoringDisable(true),
cmd::WaitUntil([] { return false; }).IgnoringDisable(true))));
EXPECT_EQ(true, command.RunsWhenDisabled());
}
TYPED_TEST_P(MultiCompositionRunsWhenDisabledTest, AllFalse) {
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] { return false; }).IgnoringDisable(false),
cmd::WaitUntil([] { return false; }).IgnoringDisable(false),
cmd::WaitUntil([] { return false; }).IgnoringDisable(false))));
EXPECT_EQ(false, command.RunsWhenDisabled());
}
TYPED_TEST_P(MultiCompositionRunsWhenDisabledTest, TwoTrueOneFalse) {
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] { return false; }).IgnoringDisable(true),
cmd::WaitUntil([] { return false; }).IgnoringDisable(true),
cmd::WaitUntil([] { return false; }).IgnoringDisable(false))));
EXPECT_EQ(false, command.RunsWhenDisabled());
}
TYPED_TEST_P(MultiCompositionRunsWhenDisabledTest, TwoFalseOneTrue) {
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] { return false; }).IgnoringDisable(false),
cmd::WaitUntil([] { return false; }).IgnoringDisable(false),
cmd::WaitUntil([] { return false; }).IgnoringDisable(true))));
EXPECT_EQ(false, command.RunsWhenDisabled());
}
REGISTER_TYPED_TEST_SUITE_P(MultiCompositionRunsWhenDisabledTest, OneTrue,
OneFalse, AllTrue, AllFalse, TwoTrueOneFalse,
TwoFalseOneTrue);
template <typename T>
class MultiCompositionInterruptibilityTest
: public SingleCompositionInterruptibilityTest<T> {};
TYPED_TEST_SUITE_P(MultiCompositionInterruptibilityTest);
TYPED_TEST_P(MultiCompositionInterruptibilityTest, AllCancelSelf) {
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf),
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf),
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf))));
EXPECT_EQ(Command::InterruptionBehavior::kCancelSelf,
command.GetInterruptionBehavior());
}
TYPED_TEST_P(MultiCompositionInterruptibilityTest, AllCancelIncoming) {
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelIncoming),
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelIncoming),
cmd::WaitUntil([] { return false; })
.WithInterruptBehavior(
Command::InterruptionBehavior::kCancelIncoming))));
EXPECT_EQ(Command::InterruptionBehavior::kCancelIncoming,
command.GetInterruptionBehavior());
}
TYPED_TEST_P(MultiCompositionInterruptibilityTest, TwoCancelSelfOneIncoming) {
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf),
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf),
cmd::WaitUntil([] { return false; })
.WithInterruptBehavior(
Command::InterruptionBehavior::kCancelIncoming))));
EXPECT_EQ(Command::InterruptionBehavior::kCancelSelf,
command.GetInterruptionBehavior());
}
TYPED_TEST_P(MultiCompositionInterruptibilityTest, TwoCancelIncomingOneSelf) {
TypeParam command = TypeParam(CommandPtr::UnwrapVector(cmd::impl::MakeVector(
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelIncoming),
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelIncoming),
cmd::WaitUntil([] {
return false;
}).WithInterruptBehavior(Command::InterruptionBehavior::kCancelSelf))));
EXPECT_EQ(Command::InterruptionBehavior::kCancelSelf,
command.GetInterruptionBehavior());
}
REGISTER_TYPED_TEST_SUITE_P(MultiCompositionInterruptibilityTest, AllCancelSelf,
AllCancelIncoming, TwoCancelSelfOneIncoming,
TwoCancelIncomingOneSelf);
#define INSTANTIATE_MULTI_COMMAND_COMPOSITION_TEST_SUITE(Suite, \
CompositionType) \
INSTANTIATE_TYPED_TEST_SUITE_P(Suite, MultiCompositionInterruptibilityTest, \
CompositionType); \
INSTANTIATE_TYPED_TEST_SUITE_P(Suite, MultiCompositionRunsWhenDisabledTest, \
CompositionType);
} // namespace multi
} // namespace frc2

View File

@@ -3,6 +3,7 @@
// the WPILib BSD license file in the root directory of this project.
#include "CommandTestBase.h"
#include "CompositionTestBase.h"
#include "frc2/command/InstantCommand.h"
#include "frc2/command/ParallelCommandGroup.h"
#include "frc2/command/WaitUntilCommand.h"
@@ -115,3 +116,6 @@ TEST_F(ParallelCommandGroupTest, ParallelGroupRequirement) {
EXPECT_TRUE(scheduler.IsScheduled(&command3));
EXPECT_FALSE(scheduler.IsScheduled(&group));
}
INSTANTIATE_MULTI_COMMAND_COMPOSITION_TEST_SUITE(ParallelCommandGroupTest,
ParallelCommandGroup);

View File

@@ -3,6 +3,7 @@
// the WPILib BSD license file in the root directory of this project.
#include "CommandTestBase.h"
#include "CompositionTestBase.h"
#include "frc2/command/InstantCommand.h"
#include "frc2/command/ParallelDeadlineGroup.h"
#include "frc2/command/WaitUntilCommand.h"
@@ -131,3 +132,23 @@ TEST_F(ParallelDeadlineGroupTest, ParallelDeadlineRequirement) {
EXPECT_TRUE(scheduler.IsScheduled(&command3));
EXPECT_FALSE(scheduler.IsScheduled(&group));
}
class TestableDeadlineCommand : public ParallelDeadlineGroup {
static ParallelDeadlineGroup ToCommand(
std::vector<std::unique_ptr<Command>>&& commands) {
std::vector<std::unique_ptr<Command>> vec;
std::unique_ptr<Command> deadline = std::move(commands[0]);
for (unsigned int i = 1; i < commands.size(); i++) {
vec.emplace_back(std::move(commands[i]));
}
return ParallelDeadlineGroup(std::move(deadline), std::move(vec));
}
public:
explicit TestableDeadlineCommand(
std::vector<std::unique_ptr<Command>>&& commands)
: ParallelDeadlineGroup(ToCommand(std::move(commands))) {}
};
INSTANTIATE_MULTI_COMMAND_COMPOSITION_TEST_SUITE(ParallelDeadlineGroupTest,
TestableDeadlineCommand);

View File

@@ -3,6 +3,7 @@
// the WPILib BSD license file in the root directory of this project.
#include "CommandTestBase.h"
#include "CompositionTestBase.h"
#include "frc2/command/InstantCommand.h"
#include "frc2/command/ParallelRaceGroup.h"
#include "frc2/command/SequentialCommandGroup.h"
@@ -202,3 +203,6 @@ TEST_F(ParallelRaceGroupTest, ParallelRaceScheduleTwice) {
EXPECT_FALSE(scheduler.IsScheduled(&group));
}
INSTANTIATE_MULTI_COMMAND_COMPOSITION_TEST_SUITE(ParallelRaceGroupTest,
ParallelRaceGroup);

View File

@@ -3,8 +3,9 @@
// the WPILib BSD license file in the root directory of this project.
#include "CommandTestBase.h"
#include "frc2/command/Commands.h"
#include "CompositionTestBase.h"
#include "frc2/command/FunctionalCommand.h"
#include "frc2/command/RepeatCommand.h"
using namespace frc2;
class RepeatCommandTest : public CommandTestBase {};
@@ -61,30 +62,5 @@ TEST_F(RepeatCommandTest, CallsMethodsCorrectly) {
EXPECT_EQ(1, endCounter);
}
class RepeatCommandInterruptibilityTest
: public CommandTestBaseWithParam<Command::InterruptionBehavior> {};
TEST_P(RepeatCommandInterruptibilityTest, Interruptibility) {
CommandPtr command = cmd::WaitUntil([] { return false; })
.WithInterruptBehavior(GetParam())
.Repeatedly();
EXPECT_EQ(GetParam(), command.get()->GetInterruptionBehavior());
}
INSTANTIATE_TEST_SUITE_P(
RepeatCommandTests, RepeatCommandInterruptibilityTest,
testing::Values(Command::InterruptionBehavior::kCancelIncoming,
Command::InterruptionBehavior::kCancelSelf));
class RepeatCommandRunsWhenDisabledTest
: public CommandTestBaseWithParam<bool> {};
TEST_P(RepeatCommandRunsWhenDisabledTest, RunsWhenDisabled) {
CommandPtr command = cmd::WaitUntil([] { return false; })
.IgnoringDisable(GetParam())
.Repeatedly();
EXPECT_EQ(GetParam(), command.get()->RunsWhenDisabled());
}
INSTANTIATE_TEST_SUITE_P(RepeatCommandTests, RepeatCommandRunsWhenDisabledTest,
testing::Bool());
INSTANTIATE_SINGLE_COMMAND_COMPOSITION_TEST_SUITE(RepeatCommandTest,
RepeatCommand);

View File

@@ -3,6 +3,7 @@
// the WPILib BSD license file in the root directory of this project.
#include "CommandTestBase.h"
#include "CompositionTestBase.h"
#include "frc2/command/ConditionalCommand.h"
#include "frc2/command/InstantCommand.h"
#include "frc2/command/SelectCommand.h"
@@ -57,3 +58,24 @@ TEST_F(SelectCommandTest, SelectCommandRequirement) {
EXPECT_TRUE(scheduler.IsScheduled(&command3));
EXPECT_FALSE(scheduler.IsScheduled(&select));
}
class TestableSelectCommand : public SelectCommand<int> {
static std::vector<std::pair<int, std::unique_ptr<Command>>> ZipVector(
std::vector<std::unique_ptr<Command>>&& commands) {
std::vector<std::pair<int, std::unique_ptr<Command>>> vec;
int index = 0;
for (auto&& command : commands) {
vec.emplace_back(std::make_pair(index, std::move(command)));
index++;
}
return vec;
}
public:
explicit TestableSelectCommand(
std::vector<std::unique_ptr<Command>>&& commands)
: SelectCommand([] { return 0; }, ZipVector(std::move(commands))) {}
};
INSTANTIATE_MULTI_COMMAND_COMPOSITION_TEST_SUITE(SelectCommandTest,
TestableSelectCommand);

View File

@@ -3,6 +3,7 @@
// the WPILib BSD license file in the root directory of this project.
#include "CommandTestBase.h"
#include "CompositionTestBase.h"
#include "frc2/command/InstantCommand.h"
#include "frc2/command/SequentialCommandGroup.h"
#include "frc2/command/WaitUntilCommand.h"
@@ -132,3 +133,6 @@ TEST_F(SequentialCommandGroupTest, SequentialGroupRequirement) {
EXPECT_TRUE(scheduler.IsScheduled(&command3));
EXPECT_FALSE(scheduler.IsScheduled(&group));
}
INSTANTIATE_MULTI_COMMAND_COMPOSITION_TEST_SUITE(SequentialCommandGroupTest,
SequentialCommandGroup);