Add fluent builders for more flexibly adding data to Shuffleboard (#1022)

This commit is contained in:
Sam Carlberg
2018-09-28 04:18:18 -04:00
committed by Peter Johnson
parent ac7dfa5042
commit 175c6c1f01
51 changed files with 3120 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj.shuffleboard;
import edu.wpi.first.wpilibj.SendableBase;
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
/**
* A mock sendable that marks itself as an actuator.
*/
public class MockActuatorSendable extends SendableBase {
public MockActuatorSendable(String name) {
super(false);
setName(name);
}
@Override
public void initSendable(SendableBuilder builder) {
builder.setActuator(true);
}
}

View File

@@ -0,0 +1,115 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj.shuffleboard;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import edu.wpi.first.networktables.NetworkTableEntry;
import edu.wpi.first.networktables.NetworkTableInstance;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ShuffleboardInstanceTest {
private NetworkTableInstance m_ntInstance;
private ShuffleboardInstance m_shuffleboardInstance;
@BeforeEach
void setupInstance() {
m_ntInstance = NetworkTableInstance.create();
m_shuffleboardInstance = new ShuffleboardInstance(m_ntInstance);
}
@AfterEach
void tearDownInstance() {
m_ntInstance.close();
}
@Test
void testPathFluent() {
NetworkTableEntry entry = m_shuffleboardInstance.getTab("Tab Title")
.getLayout("List", "List Layout")
.add("Data", "string")
.withWidget("Text View")
.getEntry();
assertAll(
() -> assertEquals("string", entry.getString(null), "Wrong entry value"),
() -> assertEquals("/Shuffleboard/Tab Title/List Layout/Data", entry.getName(),
"Entry path generated incorrectly"));
}
@Test
void testNestedLayoutsFluent() {
NetworkTableEntry entry = m_shuffleboardInstance.getTab("Tab")
.getLayout("List", "First")
.getLayout("List", "Second")
.getLayout("List", "Third")
.getLayout("List", "Fourth")
.add("Value", "string")
.getEntry();
assertAll(
() -> assertEquals("string", entry.getString(null), "Wrong entry value"),
() -> assertEquals("/Shuffleboard/Tab/First/Second/Third/Fourth/Value", entry.getName(),
"Entry path generated incorrectly"));
}
@Test
void testNestedLayoutsOop() {
ShuffleboardTab tab = m_shuffleboardInstance.getTab("Tab");
ShuffleboardLayout first = tab.getLayout("List", "First");
ShuffleboardLayout second = first.getLayout("List", "Second");
ShuffleboardLayout third = second.getLayout("List", "Third");
ShuffleboardLayout fourth = third.getLayout("List", "Fourth");
SimpleWidget widget = fourth.add("Value", "string");
NetworkTableEntry entry = widget.getEntry();
assertAll(
() -> assertEquals("string", entry.getString(null), "Wrong entry value"),
() -> assertEquals("/Shuffleboard/Tab/First/Second/Third/Fourth/Value", entry.getName(),
"Entry path generated incorrectly"));
}
@Test
void testLayoutTypeIsSet() {
String layoutType = "Type";
m_shuffleboardInstance.getTab("Tab")
.getLayout(layoutType, "Title");
m_shuffleboardInstance.update();
NetworkTableEntry entry = m_ntInstance.getEntry(
"/Shuffleboard/.metadata/Tab/Title/PreferredComponent");
assertEquals(layoutType, entry.getString("Not Set"), "Layout type not set");
}
@Test
void testNestedActuatorWidgetsAreDisabled() {
m_shuffleboardInstance.getTab("Tab")
.getLayout("Layout", "Title")
.add(new MockActuatorSendable("Actuator"));
NetworkTableEntry controllableEntry =
m_ntInstance.getEntry("/Shuffleboard/Tab/Title/Actuator/.controllable");
m_shuffleboardInstance.update();
// Note: we use the unsafe `getBoolean()` method because if the value is NOT a boolean, or if it
// is not present, then something has clearly gone very, very wrong
boolean controllable = controllableEntry.getValue().getBoolean();
// Sanity check
assertTrue(controllable, "The nested actuator widget should be enabled by default");
m_shuffleboardInstance.disableActuatorWidgets();
controllable = controllableEntry.getValue().getBoolean();
assertFalse(controllable, "The nested actuator widget should have been disabled");
}
}

View File

@@ -0,0 +1,152 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj.shuffleboard;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import edu.wpi.first.networktables.NetworkTableEntry;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.wpilibj.Sendable;
import edu.wpi.first.wpilibj.command.InstantCommand;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
@SuppressWarnings({"PMD.TooManyMethods"})
public class ShuffleboardTabTest {
private NetworkTableInstance m_ntInstance;
private ShuffleboardTab m_tab;
private ShuffleboardInstance m_instance;
@BeforeEach
void setup() {
m_ntInstance = NetworkTableInstance.create();
m_instance = new ShuffleboardInstance(m_ntInstance);
m_tab = m_instance.getTab("Tab");
}
@AfterEach
void tearDown() {
m_ntInstance.close();
}
@Test
void testAddDouble() {
NetworkTableEntry entry = m_tab.add("Double", 1.0).getEntry();
assertAll(
() -> assertEquals("/Shuffleboard/Tab/Double", entry.getName()),
() -> assertEquals(1.0, entry.getValue().getDouble()));
}
@Test
void testAddInteger() {
NetworkTableEntry entry = m_tab.add("Int", 1).getEntry();
assertAll(
() -> assertEquals("/Shuffleboard/Tab/Int", entry.getName()),
() -> assertEquals(1.0, entry.getValue().getDouble()));
}
@Test
void testAddLong() {
NetworkTableEntry entry = m_tab.add("Long", 1L).getEntry();
assertAll(
() -> assertEquals("/Shuffleboard/Tab/Long", entry.getName()),
() -> assertEquals(1.0, entry.getValue().getDouble()));
}
@Test
void testAddBoolean() {
NetworkTableEntry entry = m_tab.add("Bool", false).getEntry();
assertAll(
() -> assertEquals("/Shuffleboard/Tab/Bool", entry.getName()),
() -> assertFalse(entry.getValue().getBoolean()));
}
@Test
void testAddString() {
NetworkTableEntry entry = m_tab.add("String", "foobar").getEntry();
assertAll(
() -> assertEquals("/Shuffleboard/Tab/String", entry.getName()),
() -> assertEquals("foobar", entry.getValue().getString()));
}
@Test
void testAddNamedSendableWithProperties() {
Sendable sendable = new InstantCommand("Command");
String widgetType = "Command Widget";
m_tab.add(sendable)
.withWidget(widgetType)
.withProperties(mapOf("foo", 1234, "bar", "baz"));
m_instance.update();
String meta = "/Shuffleboard/.metadata/Tab/Command";
assertAll(
() -> assertEquals(1234,
m_ntInstance.getEntry(meta + "/Properties/foo").getDouble(-1),
"Property 'foo' not set correctly"),
() -> assertEquals("baz",
m_ntInstance.getEntry(meta + "/Properties/bar").getString(null),
"Property 'bar' not set correctly"),
() -> assertEquals(widgetType,
m_ntInstance.getEntry(meta + "/PreferredComponent").getString(null),
"Preferred component not set correctly"));
}
@Test
void testAddNumberArray() {
NetworkTableEntry entry = m_tab.add("DoubleArray", new double[]{1, 2, 3}).getEntry();
assertAll(
() -> assertEquals("/Shuffleboard/Tab/DoubleArray", entry.getName()),
() -> assertArrayEquals(new double[]{1, 2, 3}, entry.getValue().getDoubleArray()));
}
@Test
void testAddBooleanArray() {
NetworkTableEntry entry = m_tab.add("BoolArray", new boolean[]{true, false}).getEntry();
assertAll(
() -> assertEquals("/Shuffleboard/Tab/BoolArray", entry.getName()),
() -> assertArrayEquals(new boolean[]{true, false}, entry.getValue().getBooleanArray()));
}
@Test
void testAddStringArray() {
NetworkTableEntry entry = m_tab.add("StringArray", new String[]{"foo", "bar"}).getEntry();
assertAll(
() -> assertEquals("/Shuffleboard/Tab/StringArray", entry.getName()),
() -> assertArrayEquals(new String[]{"foo", "bar"}, entry.getValue().getStringArray()));
}
@Test
void testTitleDuplicates() {
m_tab.add("foo", "bar");
assertThrows(IllegalArgumentException.class, () -> m_tab.add("foo", "baz"));
}
/**
* Stub for Java 9 {@code Map.of()}.
*/
@SuppressWarnings({"unchecked", "PMD"})
private static <K, V> Map<K, V> mapOf(Object... entries) {
Map<K, V> map = new HashMap<>();
for (int i = 0; i < entries.length; i += 2) {
map.put((K) entries[i], (V) entries[i + 1]);
}
return map;
}
}

View File

@@ -0,0 +1,30 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj.shuffleboard;
import org.junit.jupiter.api.Test;
import edu.wpi.first.wpilibj.UtilityClassTest;
import static org.junit.jupiter.api.Assertions.assertSame;
public class ShuffleboardTest extends UtilityClassTest {
public ShuffleboardTest() {
super(Shuffleboard.class);
}
// Most relevant tests are in ShuffleboardTabTest
@Test
void testTabObjectsCached() {
ShuffleboardTab tab1 = Shuffleboard.getTab("testTabObjectsCached");
ShuffleboardTab tab2 = Shuffleboard.getTab("testTabObjectsCached");
assertSame(tab1, tab2, "Tab objects were not cached");
}
}