Applies Google Styleguide to Java parts of the library (#23)

This was partially applied to simulation but
simulation is a bit of a mess and has a lot of duplicated code.
This commit is contained in:
Jonathan Leitschuh
2016-05-20 12:07:40 -04:00
committed by Peter Johnson
parent 64ab6e51fe
commit a834fff7b2
266 changed files with 15574 additions and 14718 deletions

View File

@@ -7,15 +7,15 @@
package edu.wpi.first.wpilibj.test;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.MultipleFailureException;
import java.util.logging.Level;
import java.util.logging.Logger;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.RobotBase;
import edu.wpi.first.wpilibj.Timer;
@@ -23,16 +23,18 @@ import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary;
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
/**
* This class serves as a superclass for all tests that involve the hardware on
* the roboRIO. It uses an {@link BeforeClass} statement to initialize network
* communications. Any test that needs to use the hardware <b>MUST</b> extend
* from this class, to ensure that all of the hardware will be able to run.
*$
* This class serves as a superclass for all tests that involve the hardware on the roboRIO. It uses
* an {@link BeforeClass} statement to initialize network communications. Any test that needs to use
* the hardware <b>MUST</b> extend from this class, to ensure that all of the hardware will be able
* to run.
*
* @author Fredric Silberberg
* @author Jonathan Leitschuh
*/
public abstract class AbstractComsSetup {
/** Stores whether network coms have been initialized */
/**
* Stores whether network coms have been initialized.
*/
private static boolean initialized = false;
/**
@@ -40,7 +42,6 @@ public abstract class AbstractComsSetup {
* station. After starting network coms, it will loop until the driver station
* returns that the robot is enabled, to ensure that tests will be able to run
* on the hardware.
*$
*/
static {
if (!initialized) {
@@ -52,15 +53,15 @@ public abstract class AbstractComsSetup {
TestBench.out().println("Started coms");
// Wait until the robot is enabled before starting the tests
int i = 0;
int enableCounter = 0;
while (!DriverStation.getInstance().isEnabled()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// Prints the message on one line overwriting itself each time
TestBench.out().print("\rWaiting for enable: " + i++);
TestBench.out().print("\rWaiting for enable: " + enableCounter++);
}
TestBench.out().println();
@@ -74,8 +75,7 @@ public abstract class AbstractComsSetup {
protected abstract Logger getClassLogger();
/**
* This causes a stack trace to be printed as the test is running as well as
* at the end
* This causes a stack trace to be printed as the test is running as well as at the end.
*/
@Rule
public final TestWatcher getTestWatcher() {
@@ -83,8 +83,8 @@ public abstract class AbstractComsSetup {
}
/**
* Given as a way to provide a custom test watcher for a test or set of tests
*$
* Given as a way to provide a custom test watcher for a test or set of tests.
*
* @return the test watcher to use
*/
protected TestWatcher getOverridenTestWatcher() {
@@ -93,68 +93,69 @@ public abstract class AbstractComsSetup {
protected class DefaultTestWatcher extends TestWatcher {
/**
* Allows a failure to supply a custom status message to be displayed along
* with the stack trace.
* Allows a failure to supply a custom status message to be displayed along with the stack
* trace.
*/
protected void failed(Throwable e, Description description, String status) {
protected void failed(Throwable throwable, Description description, String status) {
TestBench.out().println();
// Instance of is the best way I know to retrieve this data.
if (e instanceof MultipleFailureException) {
if (throwable instanceof MultipleFailureException) {
/*
* MultipleFailureExceptions hold multiple exceptions in one exception.
* In order to properly display these stack traces we have to cast the
* throwable and work with the list of thrown exceptions stored within
* it.
*/
int i = 1; // Running exception count
int failureCount = ((MultipleFailureException) e).getFailures().size();
for (Throwable singleThrown : ((MultipleFailureException) e).getFailures()) {
int exceptionCount = 1; // Running exception count
int failureCount = ((MultipleFailureException) throwable).getFailures().size();
for (Throwable singleThrown : ((MultipleFailureException) throwable).getFailures()) {
getClassLogger().logp(
Level.SEVERE,
description.getClassName(),
description.getMethodName(),
(i++) + "/" + failureCount + " " + description.getDisplayName() + " failed "
+ singleThrown.getMessage() + "\n" + status, singleThrown);
(exceptionCount++) + "/" + failureCount + " " + description.getDisplayName() + " "
+ "failed " + singleThrown.getMessage() + "\n" + status, singleThrown);
}
} else {
getClassLogger().logp(Level.SEVERE, description.getClassName(),
description.getMethodName(),
description.getDisplayName() + " failed " + e.getMessage() + "\n" + status, e);
description.getDisplayName() + " failed " + throwable.getMessage() + "\n" + status,
throwable);
}
super.failed(e, description);
super.failed(throwable, description);
}
/*
* (non-Javadoc)
*$
*
* @see org.junit.rules.TestWatcher#failed(java.lang.Throwable,
* org.junit.runner.Description)
*/
@Override
protected void failed(Throwable e, Description description) {
failed(e, description, "");
protected void failed(Throwable exception, Description description) {
failed(exception, description, "");
}
/*
* (non-Javadoc)
*$
*
* @see org.junit.rules.TestWatcher#starting(org.junit.runner.Description)
*/
@Override
protected void starting(Description description) {
TestBench.out().println();
// Wait until the robot is enabled before starting the next tests
int i = 0;
int enableCounter = 0;
while (!DriverStation.getInstance().isEnabled()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// Prints the message on one line overwriting itself each time
TestBench.out().print("\rWaiting for enable: " + i++);
TestBench.out().print("\rWaiting for enable: " + enableCounter++);
}
getClassLogger().logp(Level.INFO, description.getClassName(), description.getMethodName(),
"Starting");
@@ -167,12 +168,12 @@ public abstract class AbstractComsSetup {
super.succeeded(description);
}
};
}
/**
* Logs a simple message without the logger formatting associated with it.
*$
* @param level The level to log the message at
*
* @param level The level to log the message at
* @param message The message to log
*/
protected void simpleLog(Level level, String message) {
@@ -182,50 +183,50 @@ public abstract class AbstractComsSetup {
}
/**
* Provided as a replacement to lambda functions to allow for repeatable
* checks to see if a correct state is reached
*$
* @author Jonathan Leitschuh
* Provided as a replacement to lambda functions to allow for repeatable checks to see if a
* correct state is reached.
*
* @author Jonathan Leitschuh
*/
public abstract class BooleanCheck {
public BooleanCheck() {}
public BooleanCheck() {
}
/**
* Runs the enclosed code and evaluates it to determine what state it should
* return.
*$
* Runs the enclosed code and evaluates it to determine what state it should return.
*
* @return true if the code provided within the method returns true
*/
abstract public boolean getAsBoolean();
};
public abstract boolean getAsBoolean();
}
/**
* Delays until either the correct state is reached or we reach the timeout.
*$
* @param level The level to log the message at.
* @param timeout How long the delay should run before it should timeout and
* allow the test to continue
* @param message The message to accompany the delay. The message will display
* 'message' took 'timeout' seconds if it passed.
* @param correctState A method to determine if the test has reached a state
* where it is valid to continue
*
* @param level The level to log the message at.
* @param timeout How long the delay should run before it should timeout and allow the test
* to continue
* @param message The message to accompany the delay. The message will display 'message' took
* 'timeout' seconds if it passed.
* @param correctState A method to determine if the test has reached a state where it is valid to
* continue
* @return a double representing how long the delay took to run in seconds.
*/
public double delayTillInCorrectStateWithMessage(Level level, double timeout, String message,
BooleanCheck correctState) {
int i = 0;
BooleanCheck correctState) {
int timeoutIndex;
// As long as we are not in the correct state and the timeout has not been
// reached then continue to run this loop
for (i = 0; i < (timeout * 100) && !correctState.getAsBoolean(); i++) {
for (timeoutIndex = 0; timeoutIndex < (timeout * 100) && !correctState.getAsBoolean();
timeoutIndex++) {
Timer.delay(.01);
}
if (correctState.getAsBoolean()) {
simpleLog(level, message + " took " + (i * .01) + " seconds");
simpleLog(level, message + " took " + (timeoutIndex * .01) + " seconds");
} else {
simpleLog(level, message + " timed out after " + (i * .01) + " seconds");
simpleLog(level, message + " timed out after " + (timeoutIndex * .01) + " seconds");
}
return i * .01;
return timeoutIndex * .01;
}
}

View File

@@ -7,6 +7,11 @@
package edu.wpi.first.wpilibj.test;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.Request;
import org.junit.runners.Suite.SuiteClasses;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Vector;
@@ -14,33 +19,23 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.Request;
import org.junit.runners.Suite.SuiteClasses;
import org.junit.runners.model.InitializationError;
/**
* Allows tests suites and tests to be run selectively from the command line
* using a regex text pattern.
*$
* @author jonathanleitschuh
* Allows tests suites and tests to be run selectively from the command line using a regex text
* pattern.
*
* @author jonathanleitschuh
*/
public abstract class AbstractTestSuite {
private static final Logger logger = Logger.getLogger(AbstractTestSuite.class.getName());
/**
* Gets all of the classes listed within the SuiteClasses annotation. To use
* it, annotate a class with <code>@RunWith(Suite.class)</code> and
* <code>@SuiteClasses({TestClass1.class, ...})</code>. When you run this
* class, it will run all the tests in all the suite classes. When loading the
* tests using regex the test list will be generated from this annotation.
*$
* @return the list of classes listed in the
* <code>@SuiteClasses({TestClass1.class, ...})</code> annotation.
* @throws RuntimeException If the <code>@SuiteClasses</code> annotation is
* missing.
* Gets all of the classes listed within the SuiteClasses annotation. To use it, annotate a class
* with <code>@RunWith(Suite.class)</code> and <code>@SuiteClasses({TestClass1.class,
* ...})</code>. When you run this class, it will run all the tests in all the suite classes. When
* loading the tests using regex the test list will be generated from this annotation.
*
* @return the list of classes listed in the <code>@SuiteClasses({TestClass1.class, ...})</code>.
* @throws RuntimeException If the <code>@SuiteClasses</code> annotation is missing.
*/
protected List<Class<?>> getAnnotatedTestClasses() {
SuiteClasses annotation = getClass().getAnnotation(SuiteClasses.class);
@@ -66,23 +61,22 @@ public abstract class AbstractTestSuite {
}
/**
* Stores a method name and method class pair. Used when searching for methods
* matching a given regex text.
*$
* @author jonathanleitschuh
* Stores a method name and method class pair. Used when searching for methods matching a given
* regex text.
*
* @author jonathanleitschuh
*/
protected class ClassMethodPair {
public final Class<?> methodClass;
public final String methodName;
public final Class<?> m_methodClass;
public final String m_methodName;
public ClassMethodPair(Class<?> klass, Method m) {
this.methodClass = klass;
this.methodName = m.getName();
public ClassMethodPair(Class<?> klass, Method method) {
m_methodClass = klass;
m_methodName = method.getName();
}
public Request getMethodRunRequest() {
return Request.method(methodClass, methodName);
return Request.method(m_methodClass, m_methodName);
}
}
@@ -105,9 +99,9 @@ public abstract class AbstractTestSuite {
/**
* Gets all of the test classes listed in this suite. Does not include any of
* the test suites. All of these classes contain tests.
*$
* Gets all of the test classes listed in this suite. Does not include any of the test suites. All
* of these classes contain tests.
*
* @param runningList the running list of classes to prevent recursion.
* @return The list of base test classes.
*/
@@ -117,21 +111,20 @@ public abstract class AbstractTestSuite {
if (areAnySuperClassesOfTypeAbstractTestSuite(c)) {
// Create a new instance of this class so that we can retrieve its data
try {
AbstractTestSuite suite = null;
Object o = c.newInstance();
suite = (AbstractTestSuite) c.newInstance();
AbstractTestSuite suite = (AbstractTestSuite) c.newInstance();
// Add the tests from this suite that match the regex to the list of
// tests to run
runningList = suite.getAllContainedBaseTests(runningList);
} catch (InstantiationException | IllegalAccessException e) {
} catch (InstantiationException | IllegalAccessException ex) {
// This shouldn't happen unless the constructor is changed in some
// way.
logger.log(Level.SEVERE, "Test suites can not take paramaters in their constructors.", e);
logger.log(Level.SEVERE, "Test suites can not take paramaters in their constructors.",
ex);
}
} else if (c.getAnnotation(SuiteClasses.class) != null) {
logger.log(Level.SEVERE,
String.format("class '%s' must extend %s to be searchable using regex.", c.getName()),
AbstractTestSuite.class.getName());
String.format("class '%s' must extend %s to be searchable using regex.",
c.getName(), AbstractTestSuite.class.getName()));
} else { // This is a class containing tests
// so add it to the list
runningList.add(c);
@@ -141,9 +134,9 @@ public abstract class AbstractTestSuite {
}
/**
* Gets all of the test classes listed in this suite. Does not include any of
* the test suites. All of these classes contain tests.
*$
* Gets all of the test classes listed in this suite. Does not include any of the test suites. All
* of these classes contain tests.
*
* @return The list of base test classes.
*/
public List<Class<?>> getAllContainedBaseTests() {
@@ -153,10 +146,10 @@ public abstract class AbstractTestSuite {
/**
* Retrieves all of the classes listed in the
* <code>@SuiteClasses</code> annotation that match the given regex text.
*$
* @param regex the text pattern to retrieve.
* Retrieves all of the classes listed in the <code>@SuiteClasses</code> annotation that match the
* given regex text.
*
* @param regex the text pattern to retrieve.
* @param runningList the running list of classes to prevent recursion
* @return The list of classes matching the regex pattern
*/
@@ -171,9 +164,9 @@ public abstract class AbstractTestSuite {
}
/**
* Retrieves all of the classes listed in the
* <code>@SuiteClasses</code> annotation that match the given regex text.
*$
* Retrieves all of the classes listed in the <code>@SuiteClasses</code> annotation that match the
* given regex text.
*
* @param regex the text pattern to retrieve.
* @return The list of classes matching the regex pattern
*/
@@ -183,15 +176,15 @@ public abstract class AbstractTestSuite {
}
/**
* Searches through all of the suites and tests and loads only the test or
* test suites matching the regex text. This method also prevents a single
* test from being loaded multiple times by loading the suite first then
* loading tests from all non loaded suites.
*$
* Searches through all of the suites and tests and loads only the test or test suites matching
* the regex text. This method also prevents a single test from being loaded multiple times by
* loading the suite first then loading tests from all non loaded suites.
*
* @param regex the regex text to search for
* @return the list of suite and/or test classes matching the regex.
*/
private List<Class<?>> getSuiteOrTestMatchingRegex(final String regex, List<Class<?>> runningList) {
private List<Class<?>> getSuiteOrTestMatchingRegex(final String regex, List<Class<?>>
runningList) {
// Get any test suites matching the regex using the superclass methods
runningList = getAllClassMatching(regex, runningList);
@@ -206,22 +199,22 @@ public abstract class AbstractTestSuite {
// Prevents recursively adding tests/suites that have already been added
if (!runningList.contains(c)) {
try {
AbstractTestSuite suite = null;
final AbstractTestSuite suite;
// Check the class to make sure that it is not a test class
if (areAnySuperClassesOfTypeAbstractTestSuite(c)) {
// Create a new instance of this class so that we can retrieve its
// data.
Object o = c.newInstance();
suite = (AbstractTestSuite) c.newInstance();
// Add the tests from this suite that match the regex to the list of
// tests to run
runningList = suite.getSuiteOrTestMatchingRegex(regex, runningList);
}
} catch (InstantiationException | IllegalAccessException e) {
} catch (InstantiationException | IllegalAccessException ex) {
// This shouldn't happen unless the constructor is changed in some
// way.
logger.log(Level.SEVERE, "Test suites can not take paramaters in their constructors.", e);
logger.log(Level.SEVERE, "Test suites can not take paramaters in their constructors.",
ex);
}
}
}
@@ -229,11 +222,10 @@ public abstract class AbstractTestSuite {
}
/**
* Searches through all of the suites and tests and loads only the test or
* test suites matching the regex text. This method also prevents a single
* test from being loaded multiple times by loading the suite first then
* loading tests from all non loaded suites.
*$
* Searches through all of the suites and tests and loads only the test or test suites matching
* the regex text. This method also prevents a single test from being loaded multiple times by
* loading the suite first then loading tests from all non loaded suites.
*
* @param regex the regex text to search for
* @return the list of suite and/or test classes matching the regex.
*/
@@ -243,26 +235,21 @@ public abstract class AbstractTestSuite {
}
/**
* Retrieves all of the classes listed in the
* <code>@SuiteClasses</code> annotation.
*$
* Retrieves all of the classes listed in the <code>@SuiteClasses</code> annotation.
*
* @return List of SuiteClasses
* @throws RuntimeException If the <code>@SuiteClasses</code> annotation is
* missing.
* @throws RuntimeException If the <code>@SuiteClasses</code> annotation is missing.
*/
public List<Class<?>> getAllClasses() {
return getAnnotatedTestClasses();
}
/**
* Gets the name of all of the classes listed within the
* <code>@SuiteClasses</code> annotation.
*$
* Gets the name of all of the classes listed within the <code>@SuiteClasses</code> annotation.
*
* @return the list of classes.
* @throws RuntimeException If the <code>@SuiteClasses</code> annotation is
* missing.
* @throws RuntimeException If the <code>@SuiteClasses</code> annotation is missing.
*/
public List<String> getAllClassName() {
List<String> classNames = new Vector<String>();

View File

@@ -0,0 +1,157 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008-2016. 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.test;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.junit.runners.model.InitializationError;
import java.util.List;
import edu.wpi.first.wpilibj.test.AbstractTestSuite.ClassMethodPair;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
/**
* Yes, this is the test system testing itself. Functionally, this is making sure that all tests get
* run correctly when you use parametrized arguments.
*
* @author jonathanleitschuh
*/
public class AbstractTestSuiteTest {
@Ignore("Prevents ANT from trying to run these as tests")
@RunWith(Suite.class)
@SuiteClasses({FirstSampleTest.class, SecondSampleTest.class, ThirdSampleTest.class,
FourthSampleTest.class, UnusualTest.class, ExampleSubSuite.class, EmptySuite.class})
class TestForAbstractTestSuite extends AbstractTestSuite {
}
TestForAbstractTestSuite m_testSuite;
@Before
public void setUp() throws Exception {
m_testSuite = new TestForAbstractTestSuite();
}
@Test
public void testGetTestsMatchingAll() throws InitializationError {
// when
List<Class<?>> collectedTests = m_testSuite.getAllClassMatching(".*");
// then
assertEquals(7, collectedTests.size());
}
@Test
public void testGetTestsMatchingSample() throws InitializationError {
// when
List<Class<?>> collectedTests = m_testSuite.getAllClassMatching(".*Sample.*");
// then
assertEquals(4, collectedTests.size());
}
@Test
public void testGetTestsMatchingUnusual() throws InitializationError {
// when
List<Class<?>> collectedTests = m_testSuite.getAllClassMatching(".*Unusual.*");
// then
assertEquals(1, collectedTests.size());
assertEquals(UnusualTest.class, collectedTests.get(0));
}
@Test
public void testGetTestsFromSuiteMatchingAll() throws InitializationError {
// when
List<Class<?>> collectedTests = m_testSuite.getSuiteOrTestMatchingRegex(".*");
// then
assertEquals(7, collectedTests.size());
}
@Test
public void testGetTestsFromSuiteMatchingTest() throws InitializationError {
// when
List<Class<?>> collectedTests = m_testSuite.getSuiteOrTestMatchingRegex(".*Test.*");
// then
assertEquals(7, collectedTests.size());
assertThat(collectedTests, hasItems(FirstSubSuiteTest.class,
SecondSubSuiteTest.class, UnusualTest.class));
assertThat(collectedTests,
not(hasItems(new Class<?>[]{ExampleSubSuite.class, EmptySuite.class})));
}
@Test
public void testGetMethodFromTest() {
// when
List<ClassMethodPair> pairs = m_testSuite.getMethodMatching(".*Method.*");
// then
assertEquals(1, pairs.size());
assertEquals(FirstSubSuiteTest.class, pairs.get(0).m_methodClass);
assertEquals(FirstSubSuiteTest.METHODNAME, pairs.get(0).m_methodName);
}
}
@SuppressWarnings("OneTopLevelClass")
class FirstSampleTest {
}
@SuppressWarnings("OneTopLevelClass")
class SecondSampleTest {
}
@SuppressWarnings("OneTopLevelClass")
class ThirdSampleTest {
}
@SuppressWarnings("OneTopLevelClass")
class FourthSampleTest {
}
@SuppressWarnings("OneTopLevelClass")
class UnusualTest {
} // This is a member of both suites
@Ignore("Prevents ANT from trying to run these as tests")
@SuppressWarnings("OneTopLevelClass")
class FirstSubSuiteTest {
public static final String METHODNAME = "aTestMethod";
@Test
@SuppressWarnings("MethodName")
public void aTestMethod() {
}
}
@SuppressWarnings("OneTopLevelClass")
class SecondSubSuiteTest {
}
@RunWith(Suite.class)
@SuiteClasses({FirstSubSuiteTest.class, SecondSubSuiteTest.class, UnusualTest.class})
@Ignore("Prevents ANT from trying to run these as tests")
@SuppressWarnings("OneTopLevelClass")
class ExampleSubSuite extends AbstractTestSuite {
}
@Ignore("Prevents ANT from trying to run these as tests")
@RunWith(Suite.class)
@SuiteClasses({})
@SuppressWarnings("OneTopLevelClass")
class EmptySuite extends AbstractTestSuite {
}

View File

@@ -7,8 +7,6 @@
package edu.wpi.first.wpilibj.test;
import java.io.File;
import org.apache.tools.ant.BuildLogger;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
@@ -16,15 +14,21 @@ import org.apache.tools.ant.taskdefs.optional.junit.FormatterElement;
import org.apache.tools.ant.taskdefs.optional.junit.JUnitTask;
import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest;
import java.io.File;
/**
* Provides an entry point for tests to run with ANT. This allows ant to output
* JUnit XML test results for Jenkins.
*$
* @author jonathanleitschuh
* Provides an entry point for tests to run with ANT. This allows ant to output JUnit XML test
* results for Jenkins.
*
* @author jonathanleitschuh
*/
public class AntJunitLanucher {
/**
* Main entry point for jenkins
*
* @param args Arguments passed to java.
*/
public static void main(String... args) {
if (args.length == 0) {
String path =
@@ -35,7 +39,6 @@ public class AntJunitLanucher {
try {
// Create the file to store the test output
new File(pathToReports).mkdirs();
JUnitTask task = new JUnitTask();
project.setProperty("java.io.tmpdir", pathToReports);
@@ -46,6 +49,8 @@ public class AntJunitLanucher {
formatToScreen.setType(typeScreen);
formatToScreen.setUseFile(false);
formatToScreen.setOutput(System.out);
JUnitTask task = new JUnitTask();
task.addFormatter(formatToScreen);
// add a build listener to the project
@@ -73,8 +78,8 @@ public class AntJunitLanucher {
TestBench.out().println("Beginning Test Execution With ANT");
task.execute();
} catch (Exception e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
TestBench.out().println(

View File

@@ -7,19 +7,16 @@
package edu.wpi.first.wpilibj.test;
import java.util.logging.Logger;
import org.junit.Test;
import java.util.logging.Logger;
/**
* This class is designated to allow for simple testing of the library without
* the overlying testing framework. This test is NOT run as a normal part of the
* testing process and must be explicitly selected at runtime by using the
* 'quick' argument.
*$
* This test should never be committed with changes to it but can be used during
* development to aid in feature testing.
*$
* This class is designated to allow for simple testing of the library without the overlying testing
* framework. This test is NOT run as a normal part of the testing process and must be explicitly
* selected at runtime by using the 'quick' argument. This test should never be committed with
* changes to it but can be used during development to aid in feature testing.
*
* @author Jonathan Leitschuh
*/
public class QuickTest extends AbstractComsSetup {
@@ -27,7 +24,7 @@ public class QuickTest extends AbstractComsSetup {
/*
* (non-Javadoc)
*$
*
* @see edu.wpi.first.wpilibj.test.AbstractComsSetup#getClassLogger()
*/
@Override

View File

@@ -7,20 +7,22 @@
package edu.wpi.first.wpilibj.test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This JUnit Rule allows you to apply this rule to any test to allow it to run
* multiple times. This is important if you have a test that fails only
* "sometimes" and needs to be rerun to find the issue.
* This JUnit Rule allows you to apply this rule to any test to allow it to run multiple times. This
* is important if you have a test that fails only "sometimes" and needs to be rerun to find the
* issue.
*
* This code was originally found here: <a href="http://www.codeaffine.com/2013/04/10/running-junit-tests-repeatedly-without-loops/">Running JUnit Tests Repeatedly Without Loops</a>
* <p>This code was originally found here:
* <a href="http://www.codeaffine.com/2013/04/10/running-junit-tests-repeatedly-without-loops/">
* Running JUnit Tests Repeatedly Without Loops</a>
*
* @author Frank Appel
*/
@@ -28,24 +30,27 @@ public class RepeatRule implements TestRule {
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.METHOD})
public @interface Repeat {
public abstract int times();
/**
* The number of times to repeat the test.
*/
int times();
}
private static class RepeatStatement extends Statement {
private final int times;
private final Statement statement;
private final int m_times;
private final Statement m_statement;
private RepeatStatement(int times, Statement statement) {
this.times = times;
this.statement = statement;
m_times = times;
m_statement = statement;
}
@Override
public void evaluate() throws Throwable {
for (int i = 0; i < times; i++) {
statement.evaluate();
for (int i = 0; i < m_times; i++) {
m_statement.evaluate();
}
}
}

View File

@@ -34,23 +34,21 @@ import edu.wpi.first.wpilibj.fixtures.FilterOutputFixture;
import edu.wpi.first.wpilibj.fixtures.MotorEncoderFixture;
import edu.wpi.first.wpilibj.fixtures.RelayCrossConnectFixture;
import edu.wpi.first.wpilibj.fixtures.TiltPanCameraFixture;
import edu.wpi.first.wpilibj.interfaces.Gyro;
import edu.wpi.first.wpilibj.mockhardware.FakePotentiometerSource;
/**
* This class provides access to all of the elements on the test bench, for use
* in fixtures. This class is a singleton, you should use {@link #getInstance()}
* to obtain a reference to the {@code TestBench}.
* This class provides access to all of the elements on the test bench, for use in fixtures. This
* class is a singleton, you should use {@link #getInstance()} to obtain a reference to the {@code
* TestBench}.
*
* TODO: This needs to be updated to the most recent test bench setup.
* <p>TODO: This needs to be updated to the most recent test bench setup.
*
* @author Fredric Silberberg
*/
public final class TestBench {
/**
* The time that it takes to have a motor go from rotating at full speed to
* completely stopped
* The time that it takes to have a motor go from rotating at full speed to completely stopped.
*/
public static final double MOTOR_STOP_TIME = 0.25;
@@ -95,18 +93,21 @@ public final class TestBench {
public static final int kMovAvgTaps = 6;
public static final double kMovAvgExpectedOutput = -10.191644;
/** The Singleton instance of the Test Bench */
/**
* The Singleton instance of the Test Bench.
*/
private static TestBench instance = null;
/**
* The single constructor for the TestBench. This method is private in order
* to prevent multiple TestBench objects from being allocated
* The single constructor for the TestBench. This method is private in order to prevent multiple
* TestBench objects from being allocated.
*/
protected TestBench() {}
protected TestBench() {
}
/**
* Constructs a new set of objects representing a connected set of Talon
* controlled Motors and an encoder
* Constructs a new set of objects representing a connected set of Talon controlled Motors and an
* encoder.
*
* @return a freshly allocated Talon, Encoder pair
*/
@@ -137,8 +138,8 @@ public final class TestBench {
}
/**
* Constructs a new set of objects representing a connected set of Victor
* controlled Motors and an encoder
* Constructs a new set of objects representing a connected set of Victor controlled Motors and an
* encoder.
*
* @return a freshly allocated Victor, Encoder pair
*/
@@ -169,8 +170,8 @@ public final class TestBench {
}
/**
* Constructs a new set of objects representing a connected set of Jaguar
* controlled Motors and an encoder
* Constructs a new set of objects representing a connected set of Jaguar controlled Motors and an
* encoder.
*
* @return a freshly allocated Jaguar, Encoder pair
*/
@@ -232,7 +233,7 @@ public final class TestBench {
/*
* (non-Javadoc)
*$
*
* @see
* edu.wpi.first.wpilibj.fixtures.CANMotorEncoderFixture#givePowerCycleRelay
* ()
@@ -249,10 +250,9 @@ public final class TestBench {
}
/**
* Constructs a new set of objects representing a connected set of CANJaguar
* controlled Motors and an encoder<br>
* Note: The CANJaguar is not freshly allocated because the CANJaguar lacks a
* free() method
* Constructs a new set of objects representing a connected set of CANJaguar controlled Motors and
* an encoder<br> Note: The CANJaguar is not freshly allocated because the CANJaguar lacks a
* free() method.
*
* @return an existing CANJaguar and a freshly allocated Encoder
*/
@@ -302,20 +302,18 @@ public final class TestBench {
}
/**
* Gets two lists of possible DIO pairs for the two pairs
*$
* @return
* Gets two lists of possible DIO pairs for the two pairs.
*/
private List<List<Integer[]>> getDIOCrossConnect() {
List<List<Integer[]>> pairs = new ArrayList<List<Integer[]>>();
List<Integer[]> setA =
Arrays.asList(new Integer[][] {
Arrays.asList(new Integer[][]{
{new Integer(DIOCrossConnectA1), new Integer(DIOCrossConnectA2)},
{new Integer(DIOCrossConnectA2), new Integer(DIOCrossConnectA1)}});
pairs.add(setA);
List<Integer[]> setB =
Arrays.asList(new Integer[][] {
Arrays.asList(new Integer[][]{
{new Integer(DIOCrossConnectB1), new Integer(DIOCrossConnectB2)},
{new Integer(DIOCrossConnectB2), new Integer(DIOCrossConnectB1)}});
pairs.add(setB);
@@ -323,6 +321,7 @@ public final class TestBench {
return pairs;
}
@SuppressWarnings("JavadocMethod")
public static AnalogCrossConnectFixture getAnalogCrossConnectFixture() {
AnalogCrossConnectFixture analogIO = new AnalogCrossConnectFixture() {
@Override
@@ -338,6 +337,7 @@ public final class TestBench {
return analogIO;
}
@SuppressWarnings("JavadocMethod")
public static RelayCrossConnectFixture getRelayCrossConnectFixture() {
RelayCrossConnectFixture relay = new RelayCrossConnectFixture() {
@@ -360,9 +360,9 @@ public final class TestBench {
}
/**
* Return a single Collection containing all of the DIOCrossConnectFixtures in
* all two pair combinations
*$
* Return a single Collection containing all of the DIOCrossConnectFixtures in all two pair
* combinations.
*
* @return pairs of DIOCrossConnectFixtures
*/
public Collection<Integer[]> getDIOCrossConnectCollection() {
@@ -374,19 +374,16 @@ public final class TestBench {
}
/**
* Gets an array of pairs for the encoder to use using two different lists
*$
* @param listA
* @param listB
* Gets an array of pairs for the encoder to use using two different lists.
*
* @param flip whether this encoder needs to be flipped
* @return A list of different inputs to use for the tests
*/
private Collection<Integer[]> getPairArray(List<Integer[]> listA, List<Integer[]> listB,
boolean flip) {
boolean flip) {
Collection<Integer[]> encoderPortPairs = new ArrayList<Integer[]>();
for (Integer[] portPairsA : listA) {
ArrayList<Integer[]> construtorInput = new ArrayList<Integer[]>();
Integer inputs[] = new Integer[5];
Integer[] inputs = new Integer[5];
inputs[0] = portPairsA[0]; // InputA
inputs[1] = portPairsA[1]; // InputB
@@ -396,6 +393,7 @@ public final class TestBench {
inputs[4] = flip ? 0 : 1; // The flip bit
}
ArrayList<Integer[]> construtorInput = new ArrayList<Integer[]>();
construtorInput.add(inputs);
inputs = inputs.clone();
@@ -411,8 +409,8 @@ public final class TestBench {
}
/**
* Constructs the list of inputs to be used for the encoder test
*$
* Constructs the list of inputs to be used for the encoder test.
*
* @return A collection of different input pairs to use for the encoder
*/
public Collection<Integer[]> getEncoderDIOCrossConnectCollection() {
@@ -427,8 +425,7 @@ public final class TestBench {
}
/**
* Constructs a new set of objects representing a single-pole IIR filter with
* a noisy data source
* Constructs a new set of objects representing a single-pole IIR filter with a noisy data source.
*
* @return a single-pole IIR filter with a noisy data source
*/
@@ -437,15 +434,15 @@ public final class TestBench {
@Override
protected LinearDigitalFilter giveFilter(PIDSource source) {
return LinearDigitalFilter.singlePoleIIR(source,
kSinglePoleIIRTimeConstant,
kFilterStep);
kSinglePoleIIRTimeConstant,
kFilterStep);
}
};
}
/**
* Constructs a new set of objects representing a moving average filter with a
* noisy data source using a linear digital filter
* Constructs a new set of objects representing a moving average filter with a noisy data source
* using a linear digital filter.
*
* @return a moving average filter with a noisy data source
*/
@@ -459,8 +456,8 @@ public final class TestBench {
}
/**
* Constructs a new set of objects representing a single-pole IIR filter with
* a repeatable data source
* Constructs a new set of objects representing a single-pole IIR filter with a repeatable data
* source.
*
* @return a single-pole IIR filter with a repeatable data source
*/
@@ -469,15 +466,14 @@ public final class TestBench {
@Override
protected LinearDigitalFilter giveFilter(PIDSource source) {
return LinearDigitalFilter.singlePoleIIR(source,
kSinglePoleIIRTimeConstant,
kFilterStep);
kSinglePoleIIRTimeConstant,
kFilterStep);
}
};
}
/**
* Constructs a new set of objects representing a high-pass filter with a
* repeatable data source
* Constructs a new set of objects representing a high-pass filter with a repeatable data source.
*
* @return a high-pass filter with a repeatable data source
*/
@@ -486,14 +482,14 @@ public final class TestBench {
@Override
protected LinearDigitalFilter giveFilter(PIDSource source) {
return LinearDigitalFilter.highPass(source, kHighPassTimeConstant,
kFilterStep);
kFilterStep);
}
};
}
/**
* Constructs a new set of objects representing a moving average filter with a
* repeatable data source using a linear digital filter
* Constructs a new set of objects representing a moving average filter with a repeatable data
* source using a linear digital filter.
*
* @return a moving average filter with a repeatable data source
*/
@@ -507,9 +503,8 @@ public final class TestBench {
}
/**
* Gets the singleton of the TestBench. If the TestBench is not already
* allocated in constructs an new instance of it. Otherwise it returns the
* existing instance.
* Gets the singleton of the TestBench. If the TestBench is not already allocated in constructs an
* new instance of it. Otherwise it returns the existing instance.
*
* @return The Singleton instance of the TestBench
*/
@@ -521,10 +516,10 @@ public final class TestBench {
}
/**
* Provides access to the output stream for the test system. This should be
* used instead of System.out This is gives us a way to implement changes to
* where the output text of this test system is sent.
*$
* Provides access to the output stream for the test system. This should be used instead of
* System.out This is gives us a way to implement changes to where the output text of this test
* system is sent.
*
* @return The test bench global print stream.
*/
public static PrintStream out() {
@@ -532,10 +527,10 @@ public final class TestBench {
}
/**
* Provides access to the error stream for the test system. This should be
* used instead of System.err This is gives us a way to implement changes to
* where the output text of this test system is sent.
*$
* Provides access to the error stream for the test system. This should be used instead of
* System.err This is gives us a way to implement changes to where the output text of this test
* system is sent.
*
* @return The test bench global print stream.
*/
public static PrintStream err() {

View File

@@ -7,16 +7,6 @@
package edu.wpi.first.wpilibj.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import junit.framework.JUnit4TestAdapter;
import junit.runner.Version;
@@ -27,16 +17,24 @@ import org.junit.runner.notification.Failure;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import edu.wpi.first.wpilibj.WpiLibJTestSuite;
import edu.wpi.first.wpilibj.can.CANTestSuite;
import edu.wpi.first.wpilibj.command.CommandTestSuite;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboardTestSuite;
/**
* The WPILibJ Integeration Test Suite collects all of the tests to be run by
* junit. In order for a test to be run, it must be added the list of suite
* classes below. The tests will be run in the order they are listed in the
* suite classes annotation.
* The WPILibJ Integeration Test Suite collects all of the tests to be run by junit. In order for a
* test to be run, it must be added the list of suite classes below. The tests will be run in the
* order they are listed in the suite classes annotation.
*/
@RunWith(Suite.class)
// These are listed on separate lines to prevent merge conflicts
@@ -47,17 +45,19 @@ public class TestSuite extends AbstractTestSuite {
// Sets up the logging output
final InputStream inputStream = TestSuite.class.getResourceAsStream("/logging.properties");
try {
if (inputStream == null)
if (inputStream == null) {
throw new NullPointerException("./logging.properties was not loaded");
}
LogManager.getLogManager().readConfiguration(inputStream);
Logger.getAnonymousLogger().info("Loaded");
} catch (final IOException | NullPointerException e) {
} catch (final IOException | NullPointerException ex) {
Logger.getAnonymousLogger().severe("Could not load default logging.properties file");
Logger.getAnonymousLogger().severe(e.getMessage());
Logger.getAnonymousLogger().severe(ex.getMessage());
}
TestBench.out().println("Starting Tests");
}
private static final Logger WPILIBJ_ROOT_LOGGER = Logger.getLogger("edu.wpi.first.wpilibj");
private static final Logger WPILIBJ_COMMAND_ROOT_LOGGER = Logger
.getLogger("edu.wpi.first.wpilibj.command");
@@ -72,6 +72,9 @@ public class TestSuite extends AbstractTestSuite {
private static TestSuite instance = null;
/**
* Get the singleton instance of the test suite.
*/
public static TestSuite getInstance() {
if (instance == null) {
instance = new TestSuite();
@@ -80,13 +83,13 @@ public class TestSuite extends AbstractTestSuite {
}
/**
* This has to be public so that the JUnit4
* This has to be public so that the JUnit4.
*/
public TestSuite() {}
public TestSuite() {
}
/**
* Displays a help message for the user when they use the --help flag at
* runtime.
* Displays a help message for the user when they use the --help flag at runtime.
*/
protected static void displayHelp() {
StringBuilder helpMessage = new StringBuilder("Test Parameters help: \n");
@@ -104,7 +107,8 @@ public class TestSuite extends AbstractTestSuite {
+ QUICK_TEST_FLAG + " or " + CLASS_NAME_FILTER
+ " and run them the given number of times.\n");
helpMessage
.append("[NOTE] All regex uses the syntax defined by java.util.regex.Pattern. This documentation can be found at "
.append("[NOTE] All regex uses the syntax defined by java.util.regex.Pattern. This "
+ "documentation can be found at "
+ "http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html\n");
helpMessage.append("\n");
helpMessage.append("\n");
@@ -113,8 +117,8 @@ public class TestSuite extends AbstractTestSuite {
}
/**
* Lets the user know that they used the TestSuite improperly and gives
* details about how to use it correctly in the future.
* Lets the user know that they used the TestSuite improperly and gives details about how to use
* it correctly in the future.
*/
protected static void displayInvalidUsage(String message, String... args) {
StringBuilder invalidMessage = new StringBuilder("Invalid Usage: " + message + "\n");
@@ -133,16 +137,16 @@ public class TestSuite extends AbstractTestSuite {
/**
* Prints the loaded tests before they are run.
*$
*
* @param classes the classes that were loaded.
*/
protected static void printLoadedTests(final Class<?>... classes) {
StringBuilder loadedTestsMessage = new StringBuilder("The following tests were loaded:\n");
Package p = null;
Package packagE = null;
for (Class<?> c : classes) {
if (c.getPackage().equals(p)) {
p = c.getPackage();
loadedTestsMessage.append(p.getName() + "\n");
if (c.getPackage().equals(packagE)) {
packagE = c.getPackage();
loadedTestsMessage.append(packagE.getName() + "\n");
}
loadedTestsMessage.append("\t" + c.getSimpleName() + "\n");
}
@@ -151,16 +155,15 @@ public class TestSuite extends AbstractTestSuite {
/**
* Parses the arguments passed at runtime and runs the appropriate tests based
* upon these arguments
*$
* Parses the arguments passed at runtime and runs the appropriate tests based upon these
* arguments
*
* @param args the args passed into the program at runtime
* @return the restults of the tests that have run. If no tests were run then
* null is returned.
* @return the restults of the tests that have run. If no tests were run then null is returned.
*/
protected static Result parseArgsRunAndGetResult(final String[] args) {
if (args.length == 0) { // If there are no args passed at runtime then just
// run all of the tests.
// run all of the tests.
printLoadedTests(TestSuite.class);
return JUnitCore.runClasses(TestSuite.class);
}
@@ -171,7 +174,6 @@ public class TestSuite extends AbstractTestSuite {
// The class filter was passed
boolean classFilter = false;
String classRegex = "";
boolean repeatFilter = false;
int repeatCount = 1;
for (String s : args) {
@@ -180,22 +182,20 @@ public class TestSuite extends AbstractTestSuite {
methodRegex = new String(s).replace(METHOD_NAME_FILTER, "");
}
if (Pattern.matches(METHOD_REPEAT_FILTER + ".*", s)) {
repeatFilter = true;
try {
repeatCount = Integer.parseInt(new String(s).replace(METHOD_REPEAT_FILTER, ""));
} catch (NumberFormatException e) {
} catch (NumberFormatException ex) {
displayInvalidUsage("The argument passed to the repeat rule was not a valid integer.",
args);
}
}
if (Pattern.matches(CLASS_NAME_FILTER + ".*", s)) {
classFilter = true;
classRegex = new String(s).replace(CLASS_NAME_FILTER, "");
classRegex = s.replace(CLASS_NAME_FILTER, "");
}
}
ArrayList<String> argsParsed = new ArrayList<String>(Arrays.asList(args));
if (argsParsed.contains(HELP_FLAG)) {
// If the user inputs the help flag then return the help message and exit
@@ -214,46 +214,46 @@ public class TestSuite extends AbstractTestSuite {
*/
class MultipleResult extends Result {
private static final long serialVersionUID = 1L;
private final List<Failure> failures = new Vector<Failure>();
private int runCount = 0;
private int ignoreCount = 0;
private long runTime = 0;
private final List<Failure> m_failures = new ArrayList<>();
private int m_runCount = 0;
private int m_ignoreCount = 0;
private long m_runTime = 0;
@Override
public int getRunCount() {
return runCount;
return m_runCount;
}
@Override
public int getFailureCount() {
return failures.size();
return m_failures.size();
}
@Override
public long getRunTime() {
return runTime;
return m_runTime;
}
@Override
public List<Failure> getFailures() {
return failures;
return m_failures;
}
@Override
public int getIgnoreCount() {
return ignoreCount;
return m_ignoreCount;
}
/**
* Adds the given result's data to this result
*$
* @param r the result to add to this result
* Adds the given result's data to this result.
*
* @param result the result to add to this result
*/
void addResult(Result r) {
failures.addAll(r.getFailures());
runCount += r.getRunCount();
ignoreCount += r.getIgnoreCount();
runTime += r.getRunTime();
void addResult(Result result) {
m_failures.addAll(result.getFailures());
m_runCount += result.getRunCount();
m_ignoreCount += result.getIgnoreCount();
m_runTime += result.getRunTime();
}
}
@@ -261,19 +261,20 @@ public class TestSuite extends AbstractTestSuite {
if (methodFilter) {
List<ClassMethodPair> pairs = (new TestSuite()).getMethodMatching(methodRegex);
if (pairs.size() == 0) {
displayInvalidUsage("None of the arguments passed to the method name filter matched.", args);
displayInvalidUsage("None of the arguments passed to the method name filter matched.",
args);
return null;
}
// Print out the list of tests before we run them
TestBench.out().println("Running the following tests:");
Class<?> classListing = null;
for (ClassMethodPair p : pairs) {
if (!p.methodClass.equals(classListing)) {
if (!p.m_methodClass.equals(classListing)) {
// Only display the class name every time it changes
classListing = p.methodClass;
classListing = p.m_methodClass;
TestBench.out().println(classListing);
}
TestBench.out().println("\t" + p.methodName);
TestBench.out().println("\t" + p.m_methodName);
}
@@ -353,9 +354,9 @@ public class TestSuite extends AbstractTestSuite {
}
/**
* This is used by ant to get the Junit tests. This is required because the
* tests try to load using a JUnit 3 framework. JUnit4 uses annotations to
* load tests. This method allows JUnit3 to load JUnit4 tests.
* This is used by ant to get the Junit tests. This is required because the tests try to load
* using a JUnit 3 framework. JUnit4 uses annotations to load tests. This method allows JUnit3 to
* load JUnit4 tests.
*/
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(TestSuite.class);
@@ -363,8 +364,8 @@ public class TestSuite extends AbstractTestSuite {
/**
* The method called at runtime
*$
* The method called at runtime.
*
* @param args The test suites to run
*/
public static void main(String[] args) {

View File

@@ -1,13 +1,11 @@
/**
* This is the starting point for the integration testing framework. This
* package should contain classes that are not explicitly for testing the
* library but instead provide the framework that the tests can extend from.
* Every test should extend from
* {@link edu.wpi.first.wpilibj.test.AbstractComsSetup} to ensure that Network
* Communications is properly instantiated before the test is run.
*$
* The {@link edu.wpi.first.wpilibj.test.TestBench} should contain the port
* numbers for all of the hardware and these values should not be explicitly
* defined anywhere else in the testing framework.
* This is the starting point for the integration testing framework. This package should contain
* classes that are not explicitly for testing the library but instead provide the framework that
* the tests can extend from. Every test should extend from
* {@link edu.wpi.first.wpilibj.test.AbstractComsSetup}
* to ensure that Network Communications is properly instantiated before the test is run. The
* {@link edu.wpi.first.wpilibj.test.TestBench} should contain the port numbers for all of the
* hardware and these values should not be explicitly defined anywhere else in the testing
* framework.
*/
package edu.wpi.first.wpilibj.test;
package edu.wpi.first.wpilibj.test;