[cmd3] Fix commandv3 builders and add tests (#8482)

Sequential group builder had an inverted if condition causing NPEs

Parallel group builder was building parallel groups using an old ctor
signature and creating groups that didn't match what was specified in
the builder

Test coverage has been added for both builder types
This commit is contained in:
Sam Carlberg
2025-12-17 01:23:04 -05:00
committed by GitHub
parent 8b10a0546d
commit 6ef55654f6
6 changed files with 212 additions and 9 deletions

View File

@@ -104,4 +104,14 @@ public final class ParallelGroup implements Command {
public String toString() {
return "ParallelGroup[name=" + m_name + "]";
}
// package-private for testing
Collection<Command> getRequiredCommands() {
return m_requiredCommands;
}
Collection<Command> getOptionalCommands() {
return m_optionalCommands;
}
}

View File

@@ -21,7 +21,7 @@ import org.wpilib.annotation.NoDiscard;
*/
@NoDiscard
public class ParallelGroupBuilder {
private final Set<Command> m_commands = new LinkedHashSet<>();
private final Set<Command> m_optionalCommands = new LinkedHashSet<>();
private final Set<Command> m_requiredCommands = new LinkedHashSet<>();
private BooleanSupplier m_endCondition;
@@ -44,7 +44,7 @@ public class ParallelGroupBuilder {
requireNonNullParam(commands[i], "commands[" + i + "]", "ParallelGroupBuilder.optional");
}
m_commands.addAll(Arrays.asList(commands));
m_optionalCommands.addAll(Arrays.asList(commands));
return this;
}
@@ -62,7 +62,6 @@ public class ParallelGroupBuilder {
}
m_requiredCommands.addAll(Arrays.asList(commands));
m_commands.addAll(m_requiredCommands);
return this;
}
@@ -103,7 +102,7 @@ public class ParallelGroupBuilder {
public ParallelGroup named(String name) {
requireNonNullParam(name, "name", "ParallelGroupBuilder.named");
var group = new ParallelGroup(name, m_commands, m_requiredCommands);
var group = new ParallelGroup(name, m_requiredCommands, m_optionalCommands);
if (m_endCondition == null) {
// No custom end condition, return the group as is
return group;
@@ -125,16 +124,14 @@ public class ParallelGroupBuilder {
String required =
m_requiredCommands.stream().map(Command::name).collect(Collectors.joining(" & ", "(", ")"));
var optionalCommands = new LinkedHashSet<>(m_commands);
optionalCommands.removeAll(m_requiredCommands);
// eg "(CommandA | CommandB | CommandC)"
String optional =
optionalCommands.stream().map(Command::name).collect(Collectors.joining(" | ", "(", ")"));
m_optionalCommands.stream().map(Command::name).collect(Collectors.joining(" | ", "(", ")"));
if (m_requiredCommands.isEmpty()) {
// No required commands, pure race
return named(optional);
} else if (optionalCommands.isEmpty()) {
} else if (m_optionalCommands.isEmpty()) {
// Everything required
return named(required);
} else {

View File

@@ -77,4 +77,9 @@ public final class SequentialGroup implements Command {
public String toString() {
return "SequentialGroup[name=" + m_name + "]";
}
// package-private for testing
List<Command> getCommands() {
return m_commands;
}
}

View File

@@ -83,7 +83,7 @@ public class SequentialGroupBuilder {
*/
public Command named(String name) {
var seq = new SequentialGroup(name, m_steps);
if (m_endCondition != null) {
if (m_endCondition == null) {
// No custom end condition, return the group as is
return seq;
}