Remove CrossConn and Integration Tests (#7692)

This commit is contained in:
Thad House
2025-01-15 11:53:17 -08:00
committed by GitHub
parent f81c42e700
commit 24d6e87447
96 changed files with 0 additions and 9774 deletions

View File

@@ -1,92 +0,0 @@
# Testing steps for real hardware
trigger:
batch: true
branches:
include:
- master
stages:
- stage: Build
jobs:
- job: IntegrationTests
displayName: Integration Tests
pool:
vmImage: 'ubuntu-latest'
container:
image: wpilib/roborio-cross-ubuntu:2023-22.04
timeoutInMinutes: 0
steps:
- task: Gradle@2
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
inputs:
workingDirectory: ""
gradleWrapperFile: "gradlew"
gradleOptions: "-Xmx3072m"
publishJUnitResults: false
testResultsFiles: "**/TEST-*.xml"
tasks: "copyWpilibJIntegrationTestJarToOutput copyWpilibCTestLibrariesToOutput"
options: "-Ponlylinuxathena -PbuildServer -PskipJavaFormat"
- task: PublishPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- stage: TestBench
displayName: Test Bench
condition: false
jobs:
- job: Cpp
displayName: C++
pool: RoboRioConnections
timeoutInMinutes: 30
workspace:
clean: all
steps:
- task: DownloadPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- task: ShellScript@2
displayName: Run C++ Tests
inputs:
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
args: 'cpp -A "--gtest_output=xml:/home/admin/testResults/cppreport.xml"'
- task: PublishTestResults@2
displayName: Publish C++ Test Results
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "*.xml"
testRunTitle: "C++ Test Report"
searchFolder: "$(System.DefaultWorkingDirectory)/test-reports"
- job: Java
pool: RoboRioConnections
timeoutInMinutes: 30
workspace:
clean: all
steps:
- task: DownloadPipelineArtifact@0
inputs:
artifactName: "Integration Tests"
targetPath: "build/integrationTestFiles"
- task: ShellScript@2
displayName: Run Java Tests
inputs:
scriptPath: test-scripts/deploy-and-run-test-on-robot.sh
args: "java"
- task: PublishTestResults@2
displayName: Publish Java Test Results
inputs:
testResultsFormat: "JUnit"
testResultsFiles: "*.xml"
testRunTitle: "Java Test Report"
searchFolder: "$(System.DefaultWorkingDirectory)/test-reports"

View File

@@ -1,112 +0,0 @@
import org.gradle.language.base.internal.ProjectLayout
import edu.wpi.first.deployutils.deploy.target.RemoteTarget
import edu.wpi.first.deployutils.deploy.target.location.SshDeployLocation
import edu.wpi.first.deployutils.deploy.artifact.*
import org.gradle.internal.os.OperatingSystem
apply plugin: 'cpp'
apply plugin: 'visual-studio'
apply plugin: 'edu.wpi.first.NativeUtils'
apply plugin: ExtraTasks
apply plugin: 'edu.wpi.first.DeployUtils'
apply from: '../shared/config.gradle'
ext {
sharedCvConfigs = [crossConnIntegrationTests: []]
staticCvConfigs = [:]
useJava = false
useCpp = true
staticGtestConfigs = [crossConnIntegrationTests: []]
}
apply from: "${rootDir}/shared/opencv.gradle"
apply from: "${rootDir}/shared/googletest.gradle"
deploy {
targets {
roborio(RemoteTarget) {
directory = '/home/admin'
maxChannels = 4
locations {
ssh(SshDeployLocation) {
address = "172.22.11.2"
user = 'admin'
password = ''
ipv6 = false
}
}
artifacts {
all {
predeploy << { ctx ->
ctx.execute('/usr/local/frc/bin/frcKillRobot.sh -t')
}
postdeploy << { ctx ->
ctx.execute("sync")
ctx.execute("ldconfig")
}
}
crossConnIntegrationTests(NativeExecutableArtifact) {
libraryDirectory = '/usr/local/frc/third-party/lib'
postdeploy << { ctx ->
ctx.execute('chmod +x crossConnIntegrationTests')
}
}
}
}
}
}
model {
components {
crossConnIntegrationTests(NativeExecutableSpec) {
targetBuildTypes 'debug'
binaries.all { binary ->
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
if (binary.buildType.name == 'debug') {
deploy.targets.roborio.artifacts.crossConnIntegrationTests.binary = binary
}
binary.sources {
athenaCpp(CppSourceSet) {
source {
srcDirs = ['src/main/native/cpp']
includes = ['**/*.cpp']
}
exportedHeaders {
srcDirs = ['src/main/native/include']
includes = ['**/*.h']
}
}
}
project(':hal').addHalDependency(binary, 'shared')
project(':hal').addHalJniDependency(binary)
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
lib project: ':thirdparty:googletest', library: 'googletest', linkage: 'static'
} else {
binary.sources {
simCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/dt'
includes = ['**/*.cpp']
}
}
}
}
}
}
}
}
tasks.register('deployTests') {
try {
dependsOn tasks.named('deployCrossConnIntegrationTestsLibrariesRoborio')
dependsOn tasks.named('deployCrossConnIntegrationTestsRoborio')
} catch (ignored) {
}
}

View File

@@ -1,112 +0,0 @@
// 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.
#include <gtest/gtest.h>
#include <hal/AnalogInput.h>
#include <hal/AnalogOutput.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class AnalogCrossTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(AnalogCrossTest, AnalogCross) {
auto param = GetParam();
int32_t status = 0;
AnalogInputHandle input{param.first, &status};
ASSERT_EQ(0, status);
AnalogOutputHandle output{param.second, &status};
ASSERT_EQ(0, status);
for (double i = 0; i < 5; i += 0.1) {
HAL_SetAnalogOutput(output, i, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_NEAR(i, HAL_GetAnalogVoltage(input, &status), 0.01);
ASSERT_EQ(0, status);
}
for (double i = 5; i > 0; i -= 0.1) {
HAL_SetAnalogOutput(output, i, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_NEAR(i, HAL_GetAnalogVoltage(input, &status), 0.01);
ASSERT_EQ(0, status);
}
}
TEST(AnalogInputTest, AllocateAll) {
wpi::SmallVector<AnalogInputHandle, 21> analogHandles;
for (int i = 0; i < HAL_GetNumAnalogInputs(); i++) {
int32_t status = 0;
analogHandles.emplace_back(AnalogInputHandle(i, &status));
ASSERT_EQ(status, 0);
}
}
TEST(AnalogInputTest, MultipleAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(0, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
AnalogInputHandle handle2(0, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(AnalogInputTest, OverAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(HAL_GetNumAnalogInputs(), &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogInputTest, UnderAllocateFails) {
int32_t status = 0;
AnalogInputHandle handle(-1, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogOutputTest, AllocateAll) {
wpi::SmallVector<AnalogOutputHandle, 21> analogHandles;
for (int i = 0; i < HAL_GetNumAnalogOutputs(); i++) {
int32_t status = 0;
analogHandles.emplace_back(AnalogOutputHandle(i, &status));
ASSERT_EQ(status, 0);
}
}
TEST(AnalogOutputTest, MultipleAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(0, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
AnalogOutputHandle handle2(0, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(AnalogOutputTest, OverAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(HAL_GetNumAnalogOutputs(), &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(AnalogOutputTest, UnderAllocateFails) {
int32_t status = 0;
AnalogOutputHandle handle(-1, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
INSTANTIATE_TEST_SUITE_P(AnalogCrossConnectsTests, AnalogCrossTest,
::testing::ValuesIn(AnalogCrossConnects));

View File

@@ -1,101 +0,0 @@
// 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.
#include <gtest/gtest.h>
#include <hal/DIO.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class DIOTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(DIOTest, DIOCross) {
auto param = GetParam();
int32_t status = 0;
DIOHandle first{param.first, false, &status};
ASSERT_EQ(0, status);
DIOHandle second{param.second, true, &status};
ASSERT_EQ(0, status);
HAL_SetDIO(first, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(first, &status));
ASSERT_EQ(0, status);
ASSERT_FALSE(HAL_GetDIO(second, &status));
ASSERT_EQ(0, status);
HAL_SetDIO(first, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(second, &status));
ASSERT_EQ(0, status);
HAL_SetDIODirection(first, true, &status);
ASSERT_EQ(0, status);
HAL_SetDIODirection(second, false, &status);
ASSERT_EQ(0, status);
HAL_SetDIO(second, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(first, &status));
ASSERT_EQ(0, status);
HAL_SetDIO(second, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(first, &status));
ASSERT_EQ(0, status);
}
TEST(DIOTest, AllocateAll) {
wpi::SmallVector<DIOHandle, 32> dioHandles;
for (int i = 0; i < HAL_GetNumDigitalChannels(); i++) {
int32_t status = 0;
dioHandles.emplace_back(i, true, &status);
ASSERT_EQ(status, 0);
}
}
TEST(DIOTest, MultipleAllocateFails) {
int32_t status = 0;
DIOHandle handle(0, true, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
DIOHandle handle2(0, true, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(DIOTest, OverAllocateFails) {
int32_t status = 0;
DIOHandle handle(HAL_GetNumDigitalChannels(), true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(DIOTest, UnderAllocateFails) {
int32_t status = 0;
DIOHandle handle(-1, true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(DIOTest, CrossAllocationFails) {
int32_t status = 0;
PWMHandle pwmHandle(10, &status);
ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
DIOHandle handle(10, true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
INSTANTIATE_TEST_SUITE_P(DIOCrossConnectsTests, DIOTest,
::testing::ValuesIn(DIOCrossConnects));

View File

@@ -1,52 +0,0 @@
// 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.
#include <gtest/gtest.h>
#include <hal/HAL.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class DutyCycleTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(DutyCycleTest, DutyCycle) {
auto param = GetParam();
int32_t status = 0;
PWMHandle pwmHandle(param.first, &status);
ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
ASSERT_EQ(0, status);
// Ensure our PWM is disabled, and set up properly
HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
ASSERT_EQ(0, status);
HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
HAL_SetPWMConfigMicroseconds(pwmHandle, 5050, 2525, 2525, 2525, 0, &status);
ASSERT_EQ(0, status);
HAL_SetPWMPeriodScale(pwmHandle, 0, &status);
ASSERT_EQ(0, status);
DIOHandle dioHandle{param.second, true, &status};
ASSERT_EQ(0, status);
DutyCycleHandle dutyCycle{dioHandle, &status};
ASSERT_EQ(0, status);
HAL_SetPWMSpeed(pwmHandle, 0.5, &status);
ASSERT_EQ(0, status);
// Sleep enough time for the frequency to converge
usleep(3500000);
ASSERT_NEAR(
1000 / 5.05,
static_cast<double>(HAL_GetDutyCycleFrequency(dutyCycle, &status)), 1);
// TODO measure output
}
INSTANTIATE_TEST_SUITE_P(DutyCycleCrossConnTests, DutyCycleTest,
::testing::ValuesIn(PWMCrossConnects));

View File

@@ -1,10 +0,0 @@
// 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.
#include <gtest/gtest.h>
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -1,359 +0,0 @@
// 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.
#include <atomic>
#include <thread>
#include <utility>
#include <gtest/gtest.h>
#include <hal/DMA.h>
#include <hal/HAL.h>
#include <wpi/SmallVector.h>
#include <wpi/condition_variable.h>
#include <wpi/priority_mutex.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class PWMTest : public ::testing::TestWithParam<std::pair<int, int>> {};
void TestTimingDMA(int squelch, std::pair<int, int> param) {
// Initialize DMA
int32_t status = 0;
DMAHandle dmaHandle(&status);
ASSERT_NE(dmaHandle, HAL_kInvalidHandle);
ASSERT_EQ(0, status);
status = 0;
PWMHandle pwmHandle(param.first, &status);
ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
ASSERT_EQ(0, status);
// Ensure our PWM is disabled, and set up properly
HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
HAL_SetPWMPeriodScale(pwmHandle, squelch, &status);
unsigned int checkPeriod = 0;
switch (squelch) {
case (0):
checkPeriod = 5050;
break;
case (1):
checkPeriod = 10100;
break;
case (3):
checkPeriod = 20200;
break;
}
status = 0;
DIOHandle dioHandle(param.second, true, &status);
ASSERT_NE(dioHandle, HAL_kInvalidHandle);
HAL_AddDMADigitalSource(dmaHandle, dioHandle, &status);
ASSERT_EQ(0, status);
HAL_SetDMAExternalTrigger(dmaHandle, dioHandle,
HAL_AnalogTriggerType::HAL_Trigger_kInWindow, true,
true, &status);
ASSERT_EQ(0, status);
// Loop to test 5 speeds
for (unsigned int testWidth = 1000; testWidth < 2100; testWidth += 250) {
HAL_StartDMA(dmaHandle, 1024, &status);
ASSERT_EQ(0, status);
while (true) {
int32_t remaining = 0;
HAL_DMASample testSample;
HAL_ReadDMA(dmaHandle, &testSample, 0.01, &remaining, &status);
if (remaining == 0) {
break;
}
}
HAL_SetPWMSpeed(pwmHandle, (testWidth - 1000) / 1000.0, &status);
constexpr const int kSampleCount = 15;
HAL_DMASample dmaSamples[kSampleCount];
int readCount = 0;
while (readCount < kSampleCount) {
status = 0;
int32_t remaining = 0;
HAL_DMAReadStatus readStatus = HAL_ReadDMA(
dmaHandle, &dmaSamples[readCount], 1.0, &remaining, &status);
ASSERT_EQ(0, status);
ASSERT_EQ(HAL_DMAReadStatus::HAL_DMA_OK, readStatus);
readCount++;
}
HAL_SetPWMSpeed(pwmHandle, 0, &status);
HAL_StopDMA(dmaHandle, &status);
// Find first rising edge
int startIndex = 4;
while (startIndex < 6) {
status = 0;
auto value = HAL_GetDMASampleDigitalSource(&dmaSamples[startIndex],
dioHandle, &status);
ASSERT_EQ(0, status);
if (value) {
break;
}
startIndex++;
}
ASSERT_LT(startIndex, 6);
// Check that samples alternate
bool previous = false;
int iterationCount = 0;
for (int i = startIndex; i < startIndex + 8; i++) {
auto value =
HAL_GetDMASampleDigitalSource(&dmaSamples[i], dioHandle, &status);
ASSERT_EQ(0, status);
ASSERT_NE(previous, value);
previous = !previous;
iterationCount++;
}
ASSERT_EQ(iterationCount, 8);
iterationCount = 0;
// Check width between samples
for (int i = startIndex; i < startIndex + 8; i += 2) {
auto width = HAL_GetDMASampleTime(&dmaSamples[i + 1], &status) -
HAL_GetDMASampleTime(&dmaSamples[i], &status);
ASSERT_NEAR(testWidth, width, 10);
iterationCount++;
}
ASSERT_EQ(iterationCount, 4);
iterationCount = 0;
// Check period between samples
for (int i = startIndex; i < startIndex + 6; i += 2) {
auto period = HAL_GetDMASampleTime(&dmaSamples[i + 2], &status) -
HAL_GetDMASampleTime(&dmaSamples[i], &status);
ASSERT_NEAR(checkPeriod, period, 10);
iterationCount++;
}
ASSERT_EQ(iterationCount, 3);
}
}
struct InterruptCheckData {
wpi::SmallVector<uint64_t, 8> risingStamps;
wpi::SmallVector<uint64_t, 8> fallingStamps;
wpi::priority_mutex mutex;
wpi::condition_variable cond;
HAL_InterruptHandle handle;
};
// TODO switch this to DMA
void TestTiming(int squelch, std::pair<int, int> param) {
// Initialize interrupt
int32_t status = 0;
InterruptHandle interruptHandle(&status);
// Ensure we have a valid interrupt handle
ASSERT_NE(interruptHandle, HAL_kInvalidHandle);
status = 0;
PWMHandle pwmHandle(param.first, &status);
ASSERT_NE(pwmHandle, HAL_kInvalidHandle);
// Ensure our PWM is disabled, and set up properly
HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
HAL_SetPWMConfigMicroseconds(pwmHandle, 2000, 1000, 1000, 0, 0, &status);
HAL_SetPWMPeriodScale(pwmHandle, squelch, &status);
unsigned int checkPeriod = 0;
switch (squelch) {
case (0):
checkPeriod = 5050;
break;
case (1):
checkPeriod = 10100;
break;
case (3):
checkPeriod = 20200;
break;
}
status = 0;
DIOHandle dioHandle(param.second, true, &status);
ASSERT_NE(dioHandle, HAL_kInvalidHandle);
InterruptCheckData interruptData;
interruptData.handle = interruptHandle;
// Can use any type for the interrupt handle
HAL_RequestInterrupts(interruptHandle, dioHandle,
HAL_AnalogTriggerType::HAL_Trigger_kInWindow, &status);
HAL_SetInterruptUpSourceEdge(interruptHandle, true, true, &status);
// Loop to test 5 speeds
for (unsigned int i = 1000; i < 2100; i += 250) {
interruptData.risingStamps.clear();
interruptData.fallingStamps.clear();
std::atomic_bool runThread{true};
status = 0;
std::thread interruptThread([&]() {
while (runThread) {
int32_t threadStatus = 0;
auto mask =
HAL_WaitForInterrupt(interruptHandle, 5, true, &threadStatus);
if ((mask & 0x100) == 0x100 && interruptData.risingStamps.size() == 0 &&
interruptData.fallingStamps.size() == 0) {
// Falling edge at start of tracking. Skip
continue;
}
int32_t status = 0;
if ((mask & 0x1) == 0x1) {
auto ts = HAL_ReadInterruptRisingTimestamp(interruptHandle, &status);
// Rising Edge
interruptData.risingStamps.push_back(ts);
} else if ((mask & 0x100) == 0x100) {
auto ts = HAL_ReadInterruptFallingTimestamp(interruptHandle, &status);
// Falling Edge
interruptData.fallingStamps.push_back(ts);
}
if (interruptData.risingStamps.size() >= 4 &&
interruptData.fallingStamps.size() >= 4) {
interruptData.cond.notify_all();
runThread = false;
break;
}
}
});
// Ensure our interrupt actually got created correctly.
ASSERT_EQ(status, 0);
HAL_SetPWMSpeed(pwmHandle, (i - 1000) / 1000.0, &status);
ASSERT_EQ(status, 0);
{
std::unique_lock<wpi::priority_mutex> lock(interruptData.mutex);
// Wait for lock
// TODO: Add Timeout
auto timeout = interruptData.cond.wait_for(lock, std::chrono::seconds(2));
if (timeout == std::cv_status::timeout) {
runThread = false;
if (interruptThread.joinable()) {
interruptThread.join();
}
ASSERT_TRUE(false); // Exit test as failure on timeout
}
}
HAL_SetPWMPulseTimeMicroseconds(pwmHandle, 0, &status);
// Ensure our interrupts have the proper counts
ASSERT_EQ(interruptData.risingStamps.size(),
interruptData.fallingStamps.size());
ASSERT_GT(interruptData.risingStamps.size(), 0u);
ASSERT_EQ(interruptData.risingStamps.size() % 2, 0u);
ASSERT_EQ(interruptData.fallingStamps.size() % 2, 0u);
for (size_t j = 0; j < interruptData.risingStamps.size(); j++) {
uint64_t width =
interruptData.fallingStamps[j] - interruptData.risingStamps[j];
ASSERT_NEAR(width, i, 10);
}
for (unsigned int j = 0; j < interruptData.risingStamps.size() - 1; j++) {
uint64_t period =
interruptData.risingStamps[j + 1] - interruptData.risingStamps[j];
ASSERT_NEAR(period, checkPeriod, 10);
}
runThread = false;
if (interruptThread.joinable()) {
interruptThread.join();
}
}
}
TEST_P(PWMTest, Timing4x) {
auto param = GetParam();
TestTiming(3, param);
}
TEST_P(PWMTest, Timing2x) {
auto param = GetParam();
TestTiming(1, param);
}
TEST_P(PWMTest, Timing1x) {
auto param = GetParam();
TestTiming(0, param);
}
TEST_P(PWMTest, TimingDMA4x) {
auto param = GetParam();
TestTimingDMA(3, param);
}
TEST_P(PWMTest, TimingDMA2x) {
auto param = GetParam();
TestTimingDMA(1, param);
}
TEST_P(PWMTest, TimingDMA1x) {
auto param = GetParam();
TestTimingDMA(0, param);
}
TEST(PWMTest, AllocateAll) {
wpi::SmallVector<PWMHandle, 21> pwmHandles;
for (int i = 0; i < HAL_GetNumPWMChannels(); i++) {
int32_t status = 0;
pwmHandles.emplace_back(PWMHandle(i, &status));
ASSERT_EQ(status, 0);
}
}
TEST(PWMTest, MultipleAllocateFails) {
int32_t status = 0;
PWMHandle handle(0, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
PWMHandle handle2(0, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(PWMTest, OverAllocateFails) {
int32_t status = 0;
PWMHandle handle(HAL_GetNumPWMChannels(), &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(PWMTest, UnderAllocateFails) {
int32_t status = 0;
PWMHandle handle(-1, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(PWMTest, CrossAllocationFails) {
int32_t status = 0;
DIOHandle dioHandle(10, true, &status);
ASSERT_NE(dioHandle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
PWMHandle handle(10, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
INSTANTIATE_TEST_SUITE_P(PWMCrossConnectTests, PWMTest,
::testing::ValuesIn(PWMCrossConnects));

View File

@@ -1,50 +0,0 @@
// 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.
#include <gtest/gtest.h>
#include <hal/AnalogInput.h>
#include <hal/Relay.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class RelayAnalogTest : public ::testing::TestWithParam<std::pair<int, int>> {};
TEST_P(RelayAnalogTest, RelayAnalogCross) {
auto param = GetParam();
int32_t status = 0;
RelayHandle relay{param.first, true, &status};
ASSERT_EQ(0, status);
AnalogInputHandle analog{param.second, &status};
ASSERT_EQ(0, status);
AnalogTriggerHandle trigger{analog, &status};
ASSERT_EQ(0, status);
HAL_SetAnalogTriggerLimitsVoltage(trigger, 1.5, 3.0, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(relay, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(relay, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(relay, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetAnalogTriggerTriggerState(trigger, &status));
ASSERT_EQ(0, status);
}
INSTANTIATE_TEST_SUITE_P(RelayAnalogCrossConnectsTests, RelayAnalogTest,
::testing::ValuesIn(RelayAnalogCrossConnects));

View File

@@ -1,104 +0,0 @@
// 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.
#include <gtest/gtest.h>
#include <hal/Relay.h>
#include <wpi/SmallVector.h>
#include "CrossConnects.h"
#include "LifetimeWrappers.h"
using namespace hlt;
class RelayDigitalTest : public ::testing::TestWithParam<RelayCross> {};
TEST_P(RelayDigitalTest, RelayCross) {
auto param = GetParam();
int32_t status = 0;
RelayHandle fwd{param.Relay, true, &status};
ASSERT_EQ(0, status);
RelayHandle rev{param.Relay, false, &status};
ASSERT_EQ(0, status);
DIOHandle fwdInput{param.FwdDio, true, &status};
ASSERT_EQ(0, status);
DIOHandle revInput{param.RevDio, true, &status};
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, false, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_FALSE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, false, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_FALSE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_TRUE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, true, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, false, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_FALSE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
HAL_SetRelay(fwd, true, &status);
ASSERT_EQ(0, status);
HAL_SetRelay(rev, true, &status);
ASSERT_EQ(0, status);
usleep(1000);
ASSERT_TRUE(HAL_GetDIO(fwdInput, &status));
ASSERT_EQ(0, status);
ASSERT_TRUE(HAL_GetDIO(revInput, &status));
ASSERT_EQ(0, status);
}
TEST(RelayDigitalTest, AllocateAll) {
wpi::SmallVector<RelayHandle, 32> relayHandles;
for (int i = 0; i < HAL_GetNumRelayChannels(); i++) {
int32_t status = 0;
relayHandles.emplace_back(i / 2, i % 2, &status);
ASSERT_EQ(status, 0);
}
}
TEST(RelayDigitalTest, MultipleAllocateFails) {
int32_t status = 0;
RelayHandle handle(0, true, &status);
ASSERT_NE(handle, HAL_kInvalidHandle);
ASSERT_EQ(status, 0);
RelayHandle handle2(0, true, &status);
ASSERT_EQ(handle2, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_IS_ALLOCATED);
}
TEST(RelayDigitalTest, OverAllocateFails) {
int32_t status = 0;
RelayHandle handle(HAL_GetNumRelayChannels(), true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
TEST(RelayDigitalTest, UnderAllocateFails) {
int32_t status = 0;
RelayHandle handle(-1, true, &status);
ASSERT_EQ(handle, HAL_kInvalidHandle);
ASSERT_LAST_ERROR_STATUS(status, RESOURCE_OUT_OF_RANGE);
}
INSTANTIATE_TEST_SUITE_P(RelayDigitalCrossConnectsTests, RelayDigitalTest,
::testing::ValuesIn(RelayCrossConnects));

View File

@@ -1,74 +0,0 @@
// 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.
#include <cstdlib>
#include <cstring>
#include <thread>
#include <gtest/gtest.h>
#include <hal/HAL.h>
#include <wpi/print.h>
#include "mockds/MockDS.h"
using namespace std::chrono_literals;
class TestEnvironment : public testing::Environment {
bool m_alreadySetUp = false;
MockDS m_mockDS;
public:
TestEnvironment() {
// Only set up once. This allows gtest_repeat to be used to automatically
// repeat tests.
if (m_alreadySetUp) {
return;
}
m_alreadySetUp = true;
if (!HAL_Initialize(500, 0)) {
wpi::print(stderr, "FATAL ERROR: HAL could not be initialized\n");
std::exit(-1);
}
m_mockDS.Start();
// This sets up the network communications library to enable the driver
// 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.
HAL_ObserveUserProgramStarting();
wpi::print("Started coms\n");
int enableCounter = 0;
auto checkEnabled = []() {
HAL_ControlWord controlWord;
std::memset(&controlWord, 0, sizeof(controlWord));
HAL_GetControlWord(&controlWord);
return controlWord.enabled && controlWord.dsAttached;
};
HAL_RefreshDSData();
while (!checkEnabled()) {
if (enableCounter > 50) {
// Robot did not enable properly after 5 seconds.
// Force exit
wpi::print(stderr, " Failed to enable. Aborting\n");
std::terminate();
}
std::this_thread::sleep_for(100ms);
wpi::print("Waiting for enable: {}\n", enableCounter++);
HAL_RefreshDSData();
}
std::this_thread::sleep_for(500ms);
}
~TestEnvironment() override { m_mockDS.Stop(); }
};
testing::Environment* const environment =
testing::AddGlobalTestEnvironment(new TestEnvironment);

View File

@@ -1,86 +0,0 @@
// 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.
#include "MockDS.h"
#include <stdint.h>
#include <string_view>
#include <hal/cpp/fpga_clock.h>
#include <wpi/Logger.h>
#include <wpi/SmallVector.h>
#include <wpi/print.h>
#include <wpinet/UDPClient.h>
static void LoggerFunc(unsigned int level, const char* file, unsigned int line,
const char* msg) {
if (level == 20) {
wpi::print(stderr, "DS: {}\n", msg);
return;
}
std::string_view levelmsg;
if (level >= 50) {
levelmsg = "CRITICAL";
} else if (level >= 40) {
levelmsg = "ERROR";
} else if (level >= 30) {
levelmsg = "WARNING";
} else {
return;
}
wpi::print(stderr, "DS: {}: {} ({}:{})\n", levelmsg, msg, file, line);
}
static void generateEnabledDsPacket(wpi::SmallVectorImpl<uint8_t>& data,
uint16_t sendCount) {
data.clear();
data.push_back(sendCount >> 8);
data.push_back(sendCount);
data.push_back(0x01); // general data tag
data.push_back(0x04); // teleop enabled
data.push_back(0x10); // normal data request
data.push_back(0x00); // red 1 station
}
void MockDS::Start() {
if (m_active) {
return;
}
m_active = true;
m_thread = std::thread([&]() {
wpi::Logger logger(LoggerFunc);
wpi::UDPClient client(logger);
client.start();
auto timeout_time = hal::fpga_clock::now();
int initCount = 0;
uint16_t sendCount = 0;
wpi::SmallVector<uint8_t, 8> data;
while (m_active) {
// Keep 20ms intervals, and increase time to next interval
auto current = hal::fpga_clock::now();
while (timeout_time <= current) {
timeout_time += std::chrono::milliseconds(20);
}
std::this_thread::sleep_until(timeout_time);
generateEnabledDsPacket(data, sendCount++);
// ~10 disabled packets are required to make the robot actually enable
// 1 is definitely not enough.
if (initCount < 10) {
initCount++;
data[3] = 0;
}
client.send(data, "127.0.0.1", 1110);
}
client.shutdown();
});
}
void MockDS::Stop() {
m_active = false;
if (m_thread.joinable()) {
m_thread.join();
}
}

View File

@@ -1,23 +0,0 @@
// 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 <atomic>
#include <thread>
class MockDS {
public:
MockDS() = default;
~MockDS() { Stop(); }
MockDS(const MockDS& other) = delete;
MockDS& operator=(const MockDS& other) = delete;
void Start();
void Stop();
private:
std::thread m_thread;
std::atomic_bool m_active{false};
};

View File

@@ -1,5 +0,0 @@
// 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.
int main() {}

View File

@@ -1,62 +0,0 @@
// 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 <array>
#include <utility>
namespace hlt {
constexpr static std::array<std::pair<int, int>, 22> DIOCrossConnects{
std::pair{20, 25},
std::pair{19, 24},
std::pair{17, 13},
std::pair{16, 12},
std::pair{15, 11},
std::pair{14, 10},
std::pair{26, 2},
std::pair{27, 1},
std::pair{28, 0},
std::pair{29, 3},
std::pair{30, 4},
// Opposite direction
std::pair{25, 20},
std::pair{24, 19},
std::pair{13, 17},
std::pair{12, 16},
std::pair{11, 15},
std::pair{10, 14},
std::pair{2, 26},
std::pair{1, 27},
std::pair{0, 28},
std::pair{3, 29},
std::pair{4, 30},
};
// PWM on left, DIO on right
constexpr static std::array<std::pair<int, int>, 2> PWMCrossConnects{
std::pair{0, 18},
std::pair{16, 25},
};
// FWD only, relay on left
constexpr static std::array<std::pair<int, int>, 2> RelayAnalogCrossConnects{
std::pair{2, 0}, std::pair{3, 1}};
struct RelayCross {
int Relay;
int FwdDio;
int RevDio;
};
constexpr static std::array<RelayCross, 1> RelayCrossConnects{
RelayCross{0, 23, 22}};
// input on left
constexpr static std::array<std::pair<int, int>, 2> AnalogCrossConnects{
std::pair{2, 0}, std::pair{4, 1}};
} // namespace hlt

View File

@@ -1,86 +0,0 @@
// 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 <hal/DMA.h>
#include <hal/DutyCycle.h>
#include <hal/HAL.h>
namespace hlt {
struct InterruptHandle : hal::Handle<HAL_InterruptHandle, HAL_CleanInterrupts> {
public:
explicit InterruptHandle(int32_t* status)
: Handle{HAL_InitializeInterrupts(status)} {}
};
struct DMAHandle : hal::Handle<HAL_DMAHandle, HAL_FreeDMA> {
public:
explicit DMAHandle(int32_t* status) : Handle{HAL_InitializeDMA(status)} {}
};
struct AnalogInputHandle
: hal::Handle<HAL_AnalogInputHandle, HAL_FreeAnalogInputPort> {
public:
AnalogInputHandle(int32_t port, int32_t* status)
: Handle{HAL_InitializeAnalogInputPort(HAL_GetPort(port), nullptr,
status)} {}
};
struct AnalogOutputHandle
: hal::Handle<HAL_AnalogOutputHandle, HAL_FreeAnalogOutputPort> {
public:
AnalogOutputHandle(int32_t port, int32_t* status)
: Handle{HAL_InitializeAnalogOutputPort(HAL_GetPort(port), nullptr,
status)} {}
};
struct AnalogTriggerHandle
: hal::Handle<HAL_AnalogTriggerHandle, HAL_CleanAnalogTrigger> {
public:
explicit AnalogTriggerHandle(HAL_AnalogInputHandle port, int32_t* status)
: Handle{HAL_InitializeAnalogTrigger(port, status)} {}
};
struct DutyCycleHandle : hal::Handle<HAL_DutyCycleHandle, HAL_FreeDutyCycle> {
public:
DutyCycleHandle(HAL_DigitalHandle port, int32_t* status)
: Handle{HAL_InitializeDutyCycle(
port, HAL_AnalogTriggerType::HAL_Trigger_kInWindow, status)} {}
};
struct DIOHandle : hal::Handle<HAL_DigitalHandle, HAL_FreeDIOPort> {
public:
DIOHandle() {}
DIOHandle(int32_t port, HAL_Bool input, int32_t* status)
: Handle{
HAL_InitializeDIOPort(HAL_GetPort(port), input, nullptr, status)} {}
};
struct PWMHandle : hal::Handle<HAL_DigitalHandle, HAL_FreePWMPort> {
public:
PWMHandle() {}
PWMHandle(int32_t port, int32_t* status)
: Handle{HAL_InitializePWMPort(HAL_GetPort(port), nullptr, status)} {}
};
struct RelayHandle : hal::Handle<HAL_RelayHandle, HAL_FreeRelayPort> {
public:
RelayHandle(int32_t port, HAL_Bool fwd, int32_t* status)
: Handle{
HAL_InitializeRelayPort(HAL_GetPort(port), fwd, nullptr, status)} {}
};
#define ASSERT_LAST_ERROR_STATUS(status, x) \
do { \
ASSERT_EQ(status, HAL_USE_LAST_ERROR); \
[[maybe_unused]] \
const char* lastErrorMessageInMacro = HAL_GetLastError(&status); \
ASSERT_EQ(status, x); \
} while (0)
} // namespace hlt

View File

@@ -29,12 +29,9 @@ include 'wpigui'
include 'wpimath'
include 'wpilibc'
include 'wpilibcExamples'
include 'wpilibcIntegrationTests'
include 'wpilibjExamples'
include 'wpilibjIntegrationTests'
include 'wpilibj'
include 'wpiunits'
include 'crossConnIntegrationTests'
include 'fieldImages'
include 'glass'
include 'outlineviewer'

View File

@@ -1,4 +0,0 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
*.sh text eol=lf

View File

@@ -1,19 +0,0 @@
# WPILib Test Scripts
## Overview
These test scripts are designed to allow the user of the WPILib test framework to quickly and easily deploy and run their tests on the WPI roboRIO.
If you deploy code to the test stand using GradleRIO, you _must_ remove the build artifacts in `/home/lvuser`, or you will break the test stand.
## roboRIO Setup
The roboRIO on the test bench must be updated every time NI releases a new image.
1. [Use the roboRIO Imaging Tool to format the roboRIO with the latest image.](https://frcdocs.wpi.edu/en/stable/docs/zero-to-robot/step-3/imaging-your-roborio.html)
2. Set a static ip on the roboRIO web dashboard to `10.1.90.2`
2. Install Java on the roboRIO
1. [Download the JRE from Maven.](https://frcmaven.wpi.edu/artifactory/list/release/edu/wpi/first/jdk/)
2. Transfer the JRE ipk to the roboRIO with scp: `scp <local path> admin@roboRIO-190-FRC.local:/tmp/frcjre.ipk`
3. Install the JRE: `opkg install /tmp/frcjre.ipk`
4. Remove the ipk file: `rm /tmp/frcjre.ipk`
3. Reboot the roboRIO

View File

@@ -1,40 +0,0 @@
#!/usr/bin/env bash
#*----------------------------------------------------------------------------*#
#* Copyright (c) 2014-2019 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. *#
#*----------------------------------------------------------------------------*#
# If this is changed, update the .gitignore
# so that test results are not committed to the repo
DEFAULT_LOCAL_TEST_RESULTS_DIR=../test-reports
ROBOT_ADDRESS=admin@roboRIO-190-FRC.local
ADMIN_ROBOT_ADDRESS=admin@roboRIO-190-FRC.local
DEFAULT_LOCAL_RUN_TEST_SCRIPT="run-tests-on-robot.sh"
DEFAULT_DESTINATION_DIR=/home/admin
DEFAULT_TEST_SCP_DIR=${DEFAULT_DESTINATION_DIR}/deployedTests
DEFAULT_DESTINATION_TEST_RESULTS_DIR=${DEFAULT_DESTINATION_DIR}/testResults
# C++ test variables
DEFAULT_CPP_TEST_NAME=FRCUserProgram
DEFAULT_CPP_TEST_ARGS="--gtest_color=yes"
DEFAULT_LOCAL_CPP_TEST_FILE=../build/integrationTestFiles/cpp/FRCUserProgram
CPP_REPORT=cppreport.xml
DEFAULT_LOCAL_CPP_TEST_RESULT=${DEFAULT_LOCAL_TEST_RESULTS_DIR}/${CPP_REPORT}
DEFAULT_DESTINATION_CPP_TEST_RESULTS=${DEFAULT_DESTINATION_TEST_RESULTS_DIR}/${CPP_REPORT}
# Java test variables
DEFAULT_JAVA_TEST_NAME=FRCUserProgram.jar
DEFAULT_JAVA_TEST_ARGS=""
DEFAULT_LOCAL_JAVA_TEST_FILE=../build/integrationTestFiles/java/wpilibjIntegrationTests-all.jar
JAVA_REPORT=javareport.xml
DEFAULT_LIBRARY_NATIVE_FILES=../build/integrationTestFiles/libs
DEFAULT_LIBRARY_NATIVE_DESTINATION=/usr/local/frc/lib
DEFAULT_LOCAL_JAVA_TEST_RESULT=${DEFAULT_LOCAL_TEST_RESULTS_DIR}/${JAVA_REPORT}
DEFAULT_DESTINATION_JAVA_TEST_RESULTS=${DEFAULT_DESTINATION_TEST_RESULTS_DIR}/AntReports/TEST-edu.wpi.first.wpilibj.test.TestSuite.xml

View File

@@ -1,84 +0,0 @@
#!/usr/bin/env bash
#*----------------------------------------------------------------------------*#
#* Copyright (c) 2014-2019 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. *#
#*----------------------------------------------------------------------------*#
# Configurable variables
source config.sh
# Java variables
DEFAULT_DESTINATION_JAVA_TEST_FILE=${DEFAULT_TEST_SCP_DIR}/${DEFAULT_JAVA_TEST_NAME}
# C++ Variables
DEFAULT_DESTINATION_CPP_TEST_FILE=${DEFAULT_TEST_SCP_DIR}/${DEFAULT_CPP_TEST_NAME}
DEFAULT_DESTINATION_RUN_TEST_SCRIPT=${DEFAULT_DESTINATION_DIR}/${DEFAULT_LOCAL_RUN_TEST_SCRIPT}
usage="$(basename "$0") [-h] (java|cpp) [-A] [arg] [arg]...
A script designed to run the integration tests.
This script should only be run on the computer connected to the roboRIO.
Where:
-h Show this help text.
-A Disable language recommended arguments.
arg Additional arguments to be passed to test."
# These variables are set when the language is selected
LANGUAGE=none
LOCAL_TEST_FILE=none
DESTINATION_TEST_FILE=none
TEST_RUN_ARGS=""
DESTINATION_TEST_RESULTS=none
LOCAL_TEST_RESULTS=none
# Begin searching for options from the second parameter on
PARAM_ARGS=${@:2}
if [[ "$1" = java ]]; then
LANGUAGE=$1
LOCAL_TEST_FILE=$DEFAULT_LOCAL_JAVA_TEST_FILE
DESTINATION_TEST_FILE=$DEFAULT_DESTINATION_JAVA_TEST_FILE
DESTINATION_TEST_RESULTS=$DEFAULT_DESTINATION_JAVA_TEST_RESULTS
LOCAL_TEST_RESULTS=$DEFAULT_LOCAL_JAVA_TEST_RESULT
elif [[ "$1" = cpp ]]; then
LANGUAGE=$1
LOCAL_TEST_FILE=$DEFAULT_LOCAL_CPP_TEST_FILE
DESTINATION_TEST_FILE=$DEFAULT_DESTINATION_CPP_TEST_FILE
DESTINATION_TEST_RESULTS=$DEFAULT_DESTINATION_CPP_TEST_RESULTS
LOCAL_TEST_RESULTS=$DEFAULT_LOCAL_CPP_TEST_RESULT
elif [[ "$1" = "-h" ]]; then
printf "Usage:\n"
echo "$usage"
exit
else
printf "Invalid language selection: %s\n\n" "$1" >&2
echo "$usage" >&2
exit 1
fi
# Check if the test file to upload exists
if [[ ! -e ${LOCAL_TEST_FILE} ]]; then
printf "The test file does not exist: %s\nAre you sure that you compiled the tests??\n\n" "${LOCAL_TEST_FILE}" >&2
echo "$usage" >&2
exit 1
fi
TEST_RUN_ARGS="${@:2}"
shopt -s huponexit
# Fail if any command fails
set -e
ssh ${ROBOT_ADDRESS} "rm -R ${DEFAULT_DESTINATION_TEST_RESULTS_DIR}; mkdir ${DEFAULT_DESTINATION_TEST_RESULTS_DIR}"
scp ${DEFAULT_LIBRARY_NATIVE_FILES}/* ${ROBOT_ADDRESS}:${DEFAULT_LIBRARY_NATIVE_DESTINATION}
ssh ${ADMIN_ROBOT_ADDRESS} ldconfig
scp config.sh ${DEFAULT_LOCAL_RUN_TEST_SCRIPT} ${ROBOT_ADDRESS}:/${DEFAULT_DESTINATION_DIR}
ssh ${ROBOT_ADDRESS} "chmod a+x ${DEFAULT_DESTINATION_RUN_TEST_SCRIPT}; mkdir ${DEFAULT_TEST_SCP_DIR}; touch ${DESTINATION_TEST_FILE}"
scp ${LOCAL_TEST_FILE} ${ROBOT_ADDRESS}:${DESTINATION_TEST_FILE}
ssh ${ROBOT_ADDRESS} ${DEFAULT_DESTINATION_RUN_TEST_SCRIPT} ${LANGUAGE} -d ${DEFAULT_TEST_SCP_DIR} ${TEST_RUN_ARGS}
mkdir ${DEFAULT_LOCAL_TEST_RESULTS_DIR}; scp ${ROBOT_ADDRESS}:${DESTINATION_TEST_RESULTS} ${LOCAL_TEST_RESULTS}

View File

@@ -1,160 +0,0 @@
#!/usr/bin/env bash
#*----------------------------------------------------------------------------*#
#* Copyright (c) 2014-2019 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. *#
#*----------------------------------------------------------------------------*#
# This file is intended to be run in the DEFAULT_TEST_DIR on the roboRIO.
# Do not attempt to run this file on your local system.
# There is one file (delploy-and-run-test-on-robot.sh) that is designed to
# deploy this file along with the compiled tests for you.
set -e
# Configurable variables
source config.sh
DEFAULT_TEST_DIR=${DEFAULT_DESTINATION_DIR}
DEFAULT_PATH_TO_JRE=/usr/local/frc/JRE/bin/java
usage="$(basename "$0") [-h] (java|cpp) [-d test_dir] [-A] [arg] [arg]...
A script designed to run the integration tests.
This script should only be run on the roborio.
Where:
-h Show this help text
-d The directory where the tests have been placed.
This is done to prevent overwriting an already running program.
This script will automatically move the test into the ${DEFAULT_TEST_DIR}
directory.
Default: Assumes the test is in the same directory as this script.
-A Do not use the default arguments for the given language.
arg The arguments to be passed to test."
# Are you trying to run this program on a platform other than the roboRIO?
if [[ ! -e "${DEFAULT_TEST_DIR}" ]]; then
printf "Failed to find %s\nAre you trying to run this file on your local computer?\n" "${DEFAULT_TEST_DIR}"
printf "This script should only be run on the roboRIO.\n\n"
echo "$usage"
exit 1
fi
LANGUAGE=none
TEST_FILE=none
TEST_DIR="$DEFAULT_TEST_DIR"
# Begin searching for options from the second parameter on
PARAM_ARGS=${@:2}
# Where the test arguments start
TEST_RUN_ARGS=${@:2}
RUN_WITH_DEFAULT_ARGS=true
DEFAULT_ARGS=""
# Determine the language that we are attempting to run
if [[ "$1" = java ]]; then
LANGUAGE=$1
TEST_FILE=$DEFAULT_JAVA_TEST_NAME
DEFAULT_ARGS=$DEFAULT_JAVA_TEST_ARGS
elif [[ "$1" = cpp ]]; then
LANGUAGE=$1
TEST_FILE=$DEFAULT_CPP_TEST_NAME
DEFAULT_ARGS=$DEFAULT_CPP_TEST_ARGS
elif [[ "$1" = "-h" ]]; then
#If the first argument is the help option
#Allow it to be searhced for in getopts
PARAM_ARGS=${@}
else
printf "Invalid language selection: %s\n\n" "$1" >&2
echo "$usage" >&2
exit 1
fi
PARAM_COUNTER=1
printf "Param Args ${PARAM_ARGS}\n"
# Check for optional parameters
while getopts ':hmd:A' option $PARAM_ARGS ; do
case "$option" in
h)
# Print the help message
printf "Usage:\n"
echo "$usage"
exit
;;
A)
# Remove the default arguments
RUN_WITH_DEFAULT_ARGS=false
PARAM_COUNTER=$[$PARAM_COUNTER +1]
;;
d)
TEST_DIR=$OPTARG
# Since we are selecting the directory the run args start from the 5th argument
PARAM_COUNTER=$[$PARAM_COUNTER +2]
;;
?)
# When an unknown param is found then we are done so break
break
;;
esac
done
TEST_RUN_ARGS=${@:$[$PARAM_COUNTER +1]}
if [[ "$RUN_WITH_DEFAULT_ARGS" == true ]]; then
TEST_RUN_ARGS="${DEFAULT_ARGS} ${TEST_RUN_ARGS}"
fi
# Make sure at least two parameters are passed or four if running with -d option
if [[ $# -lt $PARAM_COUNTER ]]; then
printf "Invalid arg count. Should be %s, was %s.\n" "${PARAM_COUNTER}" "$#"
echo "$usage"
exit 1
fi
# Make sure the webserver is disabled to save memory
/usr/local/natinst/etc/init.d/systemWebServer stop
# Kill all running robot programs
killall java FRCUserProgram || true
# If we are running with the -d argument move the test to the DEFAULT_TEST_DIR
if [[ ! -e "${TEST_DIR}/${TEST_FILE}" ]]; then
printf "Failed to find %s.\nDid you copy the test file correctly?\n" "${TEST_DIR}/${TEST_FILE}"
echo "$usage"
exit 1
elif [[ $TEST_DIR != "$DEFAULT_TEST_DIR" ]]; then
mv "${TEST_DIR}/${TEST_FILE}" "${DEFAULT_TEST_DIR}"
fi
# Make sure the executable file has permission to run
# Get the serial number and FPGADeviceCode for this rio
export serialnum=`/sbin/fw_printenv -n serial#`
export eval `/sbin/fw_printenv DeviceCode FPGADeviceCode DeviceDesc TargetClass`
# Store the run command for the language
RUN_COMMAND=none
if [[ ${LANGUAGE} = java ]]; then
chmod a+x ${DEFAULT_JAVA_TEST_NAME}
RUN_COMMAND="env LD_LIBRARY_PATH=/opt/GenICam_v3_0_NI/bin/Linux32_ARM/:/usr/local/frc/lib ${DEFAULT_PATH_TO_JRE} -ea -jar ${DEFAULT_JAVA_TEST_NAME} ${TEST_RUN_ARGS}"
elif [[ ${LANGUAGE} = cpp ]]; then
chmod a+x ${DEFAULT_CPP_TEST_NAME}
RUN_COMMAND="./${DEFAULT_CPP_TEST_NAME} ${TEST_RUN_ARGS}"
fi
printf "Running: %s\n\n" "${RUN_COMMAND}"
COREDUMP_DIR=${DEFAULT_DESTINATION_TEST_RESULTS_DIR}/coredump
mkdir -p ${COREDUMP_DIR}
CORE_LOCATION=${COREDUMP_DIR}/core
echo ${CORE_LOCATION} > /proc/sys/kernel/core_pattern
ulimit -c unlimited
eval ${RUN_COMMAND}
if [[ $? -gt 127 && -e ${CORE_LOCATION} ]]; then
mv ${CORE_LOCATION} ${COREDUMP_DIR}/core.${LANGUAGE}
if [[ ${LANGUAGE} = java ]]; then
cp -p ${DEFAULT_JAVA_TEST_NAME} ${COREDUMP_DIR}/
elif [[ ${LANGUAGE} = cpp ]]; then
cp -p ${DEFAULT_CPP_TEST_NAME} ${COREDUMP_DIR}/
fi
fi

View File

@@ -1,24 +0,0 @@
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
ATHENA_SOURCES = glob(["src/main/native/cpp/**"])
NON_ATHENA_SOURCES = glob(["src/main/native/dt/**"])
cc_library(
name = "test_headers",
hdrs = glob(["src/main/native/include/**"]),
strip_include_prefix = "src/main/native/include",
)
cc_binary(
name = "wpilibcIntegrationTests",
srcs = select({
"@rules_bzlmodrio_toolchains//constraints/is_roborio:roborio": ATHENA_SOURCES,
"//conditions:default": NON_ATHENA_SOURCES,
}),
deps = [
":test_headers",
"//thirdparty/googletest:googletest.static",
"//wpilibc:wpilibc.static",
],
)

View File

@@ -1,102 +0,0 @@
import org.gradle.language.base.internal.ProjectLayout
apply plugin: 'cpp'
apply plugin: 'visual-studio'
apply plugin: 'edu.wpi.first.NativeUtils'
apply plugin: ExtraTasks
apply from: '../shared/config.gradle'
ext {
sharedCvConfigs = [wpilibcIntegrationTests: []]
staticCvConfigs = [:]
useJava = false
useCpp = true
staticGtestConfigs = [wpilibcIntegrationTests: []]
}
apply from: "${rootDir}/shared/opencv.gradle"
apply from: "${rootDir}/shared/googletest.gradle"
model {
components {
wpilibcIntegrationTests(NativeExecutableSpec) {
targetBuildTypes 'debug'
baseName = 'FRCUserProgram'
binaries.all { binary ->
if (binary.targetPlatform.name == nativeUtils.wpi.platforms.roborio) {
binary.sources {
athenaCpp(CppSourceSet) {
source {
srcDirs = ['src/main/native/cpp']
includes = ['**/*.cpp']
}
exportedHeaders {
srcDirs = ['src/main/native/include']
includes = ['**/*.h']
}
}
}
lib project: ':wpilibc', library: 'wpilibc', linkage: 'shared'
lib project: ':wpimath', library: 'wpimath', linkage: 'shared'
project(':ntcore').addNtcoreDependency(binary, 'shared')
project(':ntcore').addNtcoreJniDependency(binary)
lib project: ':cscore', library: 'cscore', linkage: 'shared'
lib project: ':cscore', library: 'cscoreJNIShared', linkage: 'shared'
project(':hal').addHalDependency(binary, 'shared')
project(':hal').addHalJniDependency(binary)
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
lib project: ':wpimath', library: 'wpimathJNIShared', linkage: 'shared'
lib project: ':wpinet', library: 'wpinetJNIShared', linkage: 'shared'
lib project: ':wpiutil', library: 'wpiutilJNIShared', linkage: 'shared'
lib project: ':wpinet', library: 'wpinet', linkage: 'shared'
lib project: ':cameraserver', library: 'cameraserver', linkage: 'shared'
lib project: ':thirdparty:googletest', library: 'googletest', linkage: 'static'
} else {
binary.sources {
simCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/dt'
includes = ['**/*.cpp']
}
}
}
}
}
}
}
}
def testOutputFolder = file("${project(':').buildDir}/integrationTestFiles")
model {
tasks {
copyWpilibCTestLibrariesToOutput(Copy) {
def task = it
$.binaries.each {
if (it in NativeExecutableBinarySpec && it.targetPlatform.name == nativeUtils.wpi.platforms.roborio && it.buildable) {
def installTask = it.tasks.install
task.dependsOn installTask
task.from(installTask.executableFile) {
into '/cpp'
}
installTask.libs.each {
if (it.absolutePath.endsWith('.debug')) {
return
}
task.from(it) {
into '/libs'
}
}
}
}
destinationDir testOutputFolder
}
// This is in a separate if statement because of what I would assume is a bug in grade.
// Will file an issue on their side.
if (!project.hasProperty('skiponlyathena') && !project.hasProperty('skiponlysystemcore')) {
build.dependsOn copyWpilibCTestLibrariesToOutput
}
}
}

View File

@@ -1,123 +0,0 @@
// 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.
#include <memory>
#include <gtest/gtest.h>
#include <units/time.h>
#include "TestBench.h"
#include "frc/AnalogInput.h"
#include "frc/AnalogOutput.h"
#include "frc/AnalogTrigger.h"
#include "frc/AsynchronousInterrupt.h"
#include "frc/Counter.h"
#include "frc/Timer.h"
static constexpr auto kDelayTime = 10_ms;
/**
* Test analog inputs and outputs by setting one and making sure the other
* matches.
*/
TEST(AnalogLoopTest, AnalogInputWorks) {
frc::AnalogInput input{TestBench::kFakeAnalogOutputChannel};
frc::AnalogOutput output{TestBench::kAnalogOutputChannel};
// Set the output voltage and check if the input measures the same voltage
for (int32_t i = 0; i < 50; i++) {
output.SetVoltage(i / 10.0);
frc::Wait(kDelayTime);
EXPECT_NEAR(output.GetVoltage(), input.GetVoltage(), 0.01);
}
}
/**
* Test if we can use an analog trigger to check if the output is within a
* range correctly.
*/
TEST(AnalogLoopTest, AnalogTriggerWorks) {
frc::AnalogInput input{TestBench::kFakeAnalogOutputChannel};
frc::AnalogOutput output{TestBench::kAnalogOutputChannel};
frc::AnalogTrigger trigger{&input};
trigger.SetLimitsVoltage(2.0, 3.0);
output.SetVoltage(1.0);
frc::Wait(kDelayTime);
EXPECT_FALSE(trigger.GetInWindow())
<< "Analog trigger is in the window (2V, 3V)";
EXPECT_FALSE(trigger.GetTriggerState()) << "Analog trigger is on";
output.SetVoltage(2.5);
frc::Wait(kDelayTime);
EXPECT_TRUE(trigger.GetInWindow())
<< "Analog trigger is not in the window (2V, 3V)";
EXPECT_FALSE(trigger.GetTriggerState()) << "Analog trigger is on";
output.SetVoltage(4.0);
frc::Wait(kDelayTime);
EXPECT_FALSE(trigger.GetInWindow())
<< "Analog trigger is in the window (2V, 3V)";
EXPECT_TRUE(trigger.GetTriggerState()) << "Analog trigger is not on";
}
/**
* Test if we can count the right number of ticks from an analog trigger with
* a counter.
*/
TEST(AnalogLoopTest, AnalogTriggerCounterWorks) {
frc::AnalogInput input{TestBench::kFakeAnalogOutputChannel};
frc::AnalogOutput output{TestBench::kAnalogOutputChannel};
frc::AnalogTrigger trigger{&input};
trigger.SetLimitsVoltage(2.0, 3.0);
frc::Counter counter{trigger};
// Turn the analog output low and high 50 times
for (int32_t i = 0; i < 50; i++) {
output.SetVoltage(1.0);
frc::Wait(kDelayTime);
output.SetVoltage(4.0);
frc::Wait(kDelayTime);
}
// The counter should be 50
EXPECT_EQ(50, counter.Get())
<< "Analog trigger counter did not count 50 ticks";
}
TEST(AnalogLoopTest, AsynchronusInterruptWorks) {
frc::AnalogInput input{TestBench::kFakeAnalogOutputChannel};
frc::AnalogOutput output{TestBench::kAnalogOutputChannel};
int32_t param = 0;
frc::AnalogTrigger trigger{&input};
trigger.SetLimitsVoltage(2.0, 3.0);
// Given an interrupt handler that sets an int32_t to 12345
std::shared_ptr<frc::AnalogTriggerOutput> triggerOutput =
trigger.CreateOutput(frc::AnalogTriggerType::kState);
frc::AsynchronousInterrupt interrupt{triggerOutput,
[&](auto a, auto b) { param = 12345; }};
interrupt.Enable();
// If the analog output moves from below to above the window
output.SetVoltage(0.0);
frc::Wait(kDelayTime);
output.SetVoltage(5.0);
// Then the int32_t should be 12345
frc::Wait(kDelayTime);
interrupt.Disable();
EXPECT_EQ(12345, param) << "The interrupt did not run.";
}

View File

@@ -1,35 +0,0 @@
// 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.
#include "frc/AnalogPotentiometer.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include "TestBench.h"
#include "frc/AnalogOutput.h"
#include "frc/RobotController.h"
#include "frc/Timer.h"
static constexpr double kScale = 270.0;
static constexpr double kAngle = 180.0;
TEST(AnalogPotentiometerTest, InitialSettings) {
frc::AnalogOutput m_fakePot{TestBench::kAnalogOutputChannel};
frc::AnalogPotentiometer m_pot{TestBench::kFakeAnalogOutputChannel, kScale};
m_fakePot.SetVoltage(0.0);
frc::Wait(100_ms);
EXPECT_NEAR(0.0, m_pot.Get(), 5.0)
<< "The potentiometer did not initialize to 0.";
}
TEST(AnalogPotentiometerTest, RangeValues) {
frc::AnalogOutput m_fakePot{TestBench::kAnalogOutputChannel};
frc::AnalogPotentiometer m_pot{TestBench::kFakeAnalogOutputChannel, kScale};
m_fakePot.SetVoltage(kAngle / kScale * frc::RobotController::GetVoltage5V());
frc::Wait(100_ms);
EXPECT_NEAR(kAngle, m_pot.Get(), 2.0)
<< "The potentiometer did not measure the correct angle.";
}

View File

@@ -1,27 +0,0 @@
// 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.
#include "frc/BuiltInAccelerometer.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include "frc/Timer.h"
static constexpr double kAccelerationTolerance = 0.1;
/**
* There's not much we can automatically test with the on-board accelerometer,
* but checking for gravity is probably good enough to tell that it's working.
*/
TEST(BuiltInAccelerometerTest, Accelerometer) {
frc::BuiltInAccelerometer accelerometer;
// The testbench sometimes shakes a little from a previous test. Give it some
// time.
frc::Wait(1_s);
ASSERT_NEAR(0.0, accelerometer.GetX(), kAccelerationTolerance);
ASSERT_NEAR(1.0, accelerometer.GetY(), kAccelerationTolerance);
ASSERT_NEAR(0.0, accelerometer.GetZ(), kAccelerationTolerance);
}

View File

@@ -1,153 +0,0 @@
// 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.
#include "frc/Counter.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include <units/time.h>
#include "TestBench.h"
#include "frc/Timer.h"
#include "frc/motorcontrol/Jaguar.h"
#include "frc/motorcontrol/Talon.h"
#include "frc/motorcontrol/Victor.h"
static constexpr auto kMotorDelay = 2.5_s;
static constexpr auto kMaxPeriod = 2_s;
class CounterTest : public testing::Test {
protected:
frc::Counter m_talonCounter{TestBench::kTalonEncoderChannelA};
frc::Counter m_victorCounter{TestBench::kVictorEncoderChannelA};
frc::Counter m_jaguarCounter{TestBench::kJaguarEncoderChannelA};
frc::Talon m_talon{TestBench::kVictorChannel};
frc::Victor m_victor{TestBench::kTalonChannel};
frc::Jaguar m_jaguar{TestBench::kJaguarChannel};
void Reset() {
m_talonCounter.Reset();
m_victorCounter.Reset();
m_jaguarCounter.Reset();
m_talon.Set(0.0);
m_victor.Set(0.0);
m_jaguar.Set(0.0);
}
};
/**
* Tests the counter by moving the motor and determining if the
* counter is counting.
*/
TEST_F(CounterTest, CountTalon) {
Reset();
/* Run the motor forward and determine if the counter is counting. */
m_talon.Set(1.0);
frc::Wait(0.5_s);
EXPECT_NE(0.0, m_talonCounter.Get()) << "The counter did not count (talon)";
/* Set the motor to 0 and determine if the counter resets to 0. */
m_talon.Set(0.0);
frc::Wait(0.5_s);
m_talonCounter.Reset();
EXPECT_FLOAT_EQ(0.0, m_talonCounter.Get())
<< "The counter did not restart to 0 (talon)";
}
TEST_F(CounterTest, CountVictor) {
Reset();
/* Run the motor forward and determine if the counter is counting. */
m_victor.Set(1.0);
frc::Wait(0.5_s);
EXPECT_NE(0.0, m_victorCounter.Get()) << "The counter did not count (victor)";
/* Set the motor to 0 and determine if the counter resets to 0. */
m_victor.Set(0.0);
frc::Wait(0.5_s);
m_victorCounter.Reset();
EXPECT_FLOAT_EQ(0.0, m_victorCounter.Get())
<< "The counter did not restart to 0 (jaguar)";
}
TEST_F(CounterTest, CountJaguar) {
Reset();
/* Run the motor forward and determine if the counter is counting. */
m_jaguar.Set(1.0);
frc::Wait(0.5_s);
EXPECT_NE(0.0, m_jaguarCounter.Get()) << "The counter did not count (jaguar)";
/* Set the motor to 0 and determine if the counter resets to 0. */
m_jaguar.Set(0.0);
frc::Wait(0.5_s);
m_jaguarCounter.Reset();
EXPECT_FLOAT_EQ(0.0, m_jaguarCounter.Get())
<< "The counter did not restart to 0 (jaguar)";
}
/**
* Tests the GetStopped and SetMaxPeriod methods by setting the Max Period and
* getting the value after a period of time.
*/
TEST_F(CounterTest, TalonGetStopped) {
Reset();
/* Set the Max Period of the counter and run the motor */
m_talonCounter.SetMaxPeriod(kMaxPeriod);
m_talon.Set(1.0);
frc::Wait(0.5_s);
EXPECT_FALSE(m_talonCounter.GetStopped()) << "The counter did not count.";
/* Stop the motor and wait until the Max Period is exceeded */
m_talon.Set(0.0);
frc::Wait(kMotorDelay);
EXPECT_TRUE(m_talonCounter.GetStopped())
<< "The counter did not stop counting.";
}
TEST_F(CounterTest, VictorGetStopped) {
Reset();
/* Set the Max Period of the counter and run the motor */
m_victorCounter.SetMaxPeriod(kMaxPeriod);
m_victor.Set(1.0);
frc::Wait(0.5_s);
EXPECT_FALSE(m_victorCounter.GetStopped()) << "The counter did not count.";
/* Stop the motor and wait until the Max Period is exceeded */
m_victor.Set(0.0);
frc::Wait(kMotorDelay);
EXPECT_TRUE(m_victorCounter.GetStopped())
<< "The counter did not stop counting.";
}
TEST_F(CounterTest, JaguarGetStopped) {
Reset();
/* Set the Max Period of the counter and run the motor */
m_jaguarCounter.SetMaxPeriod(kMaxPeriod);
m_jaguar.Set(1.0);
frc::Wait(0.5_s);
EXPECT_FALSE(m_jaguarCounter.GetStopped()) << "The counter did not count.";
/* Stop the motor and wait until the Max Period is exceeded */
m_jaguar.Set(0.0);
frc::Wait(kMotorDelay);
EXPECT_TRUE(m_jaguarCounter.GetStopped())
<< "The counter did not stop counting.";
}

View File

@@ -1,178 +0,0 @@
// 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.
#include "frc/DigitalInput.h" // NOLINT(build/include_order)
#include "frc/DigitalOutput.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include <units/math.h>
#include <units/time.h>
#include "TestBench.h"
#include "frc/AsynchronousInterrupt.h"
#include "frc/Counter.h"
#include "frc/SynchronousInterrupt.h"
#include "frc/Timer.h"
#define EXPECT_NEAR_UNITS(val1, val2, eps) \
EXPECT_LE(units::math::abs(val1 - val2), eps)
static constexpr auto kCounterTime = 1_ms;
static constexpr auto kDelayTime = 100_ms;
static constexpr auto kSynchronousInterruptTime = 2_s;
static constexpr auto kSynchronousInterruptTimeTolerance = 10_ms;
/**
* A fixture with a digital input and a digital output physically wired
* together.
*/
class DIOLoopTest : public testing::Test {
protected:
frc::DigitalInput m_input{TestBench::kLoop1InputChannel};
frc::DigitalOutput m_output{TestBench::kLoop1OutputChannel};
void Reset() { m_output.Set(false); }
};
/**
* Test the DigitalInput and DigitalOutput classes by setting the output and
* reading the input.
*/
TEST_F(DIOLoopTest, Loop) {
Reset();
m_output.Set(false);
frc::Wait(kDelayTime);
EXPECT_FALSE(m_input.Get()) << "The digital output was turned off, but "
<< "the digital input is on.";
m_output.Set(true);
frc::Wait(kDelayTime);
EXPECT_TRUE(m_input.Get()) << "The digital output was turned on, but "
<< "the digital input is off.";
}
/**
* Tests to see if the DIO PWM functionality works.
*/
TEST_F(DIOLoopTest, DIOPWM) {
Reset();
m_output.Set(false);
frc::Wait(kDelayTime);
EXPECT_FALSE(m_input.Get()) << "The digital output was turned off, but "
<< "the digital input is on.";
// Set frequency to 2.0 Hz
m_output.SetPWMRate(2.0);
// Enable PWM, but leave it off
m_output.EnablePWM(0.0);
frc::Wait(0.5_s);
m_output.UpdateDutyCycle(0.5);
frc::SynchronousInterrupt interrupt{m_output};
interrupt.SetInterruptEdges(false, true);
frc::SynchronousInterrupt::WaitResult result =
interrupt.WaitForInterrupt(3_s, true);
frc::Wait(0.5_s);
bool firstCycle = m_input.Get();
frc::Wait(0.5_s);
bool secondCycle = m_input.Get();
frc::Wait(0.5_s);
bool thirdCycle = m_input.Get();
frc::Wait(0.5_s);
bool forthCycle = m_input.Get();
frc::Wait(0.5_s);
bool fifthCycle = m_input.Get();
frc::Wait(0.5_s);
bool sixthCycle = m_input.Get();
frc::Wait(0.5_s);
bool seventhCycle = m_input.Get();
m_output.DisablePWM();
frc::Wait(0.5_s);
bool firstAfterStop = m_input.Get();
frc::Wait(0.5_s);
bool secondAfterStop = m_input.Get();
EXPECT_EQ(frc::SynchronousInterrupt::WaitResult::kFallingEdge, result)
<< "WaitForInterrupt was not falling.";
EXPECT_FALSE(firstCycle) << "Input not low after first delay";
EXPECT_TRUE(secondCycle) << "Input not high after second delay";
EXPECT_FALSE(thirdCycle) << "Input not low after third delay";
EXPECT_TRUE(forthCycle) << "Input not high after forth delay";
EXPECT_FALSE(fifthCycle) << "Input not low after fifth delay";
EXPECT_TRUE(sixthCycle) << "Input not high after sixth delay";
EXPECT_FALSE(seventhCycle) << "Input not low after seventh delay";
EXPECT_FALSE(firstAfterStop) << "Input not low after stopping first read";
EXPECT_FALSE(secondAfterStop) << "Input not low after stopping second read";
}
/**
* Test a fake "counter" that uses the DIO loop as an input to make sure the
* Counter class works
*/
TEST_F(DIOLoopTest, FakeCounter) {
Reset();
frc::Counter counter{&m_input};
EXPECT_EQ(0, counter.Get()) << "Counter did not initialize to 0.";
/* Count 100 ticks. The counter value should be 100 after this loop. */
for (int32_t i = 0; i < 100; ++i) {
m_output.Set(true);
frc::Wait(kCounterTime);
m_output.Set(false);
frc::Wait(kCounterTime);
}
EXPECT_EQ(100, counter.Get()) << "Counter did not count up to 100.";
}
TEST_F(DIOLoopTest, AsynchronousInterruptWorks) {
Reset();
int32_t param = 0;
frc::AsynchronousInterrupt interrupt(m_input,
[&](auto a, auto b) { param = 12345; });
interrupt.Enable();
// If the voltage rises
m_output.Set(false);
m_output.Set(true);
// Then the int32_t should be 12345
frc::Wait(kDelayTime);
interrupt.Disable();
EXPECT_EQ(12345, param) << "The interrupt did not run.";
}
TEST_F(DIOLoopTest, SynchronousInterruptWorks) {
Reset();
// Given a synchronous interrupt
frc::SynchronousInterrupt interrupt(m_input);
std::thread thr([this]() {
m_output.Set(false);
frc::Wait(kSynchronousInterruptTime);
m_output.Set(true);
});
// Then this thread should pause and resume after that number of seconds
frc::Timer timer;
timer.Start();
interrupt.WaitForInterrupt(kSynchronousInterruptTime + 1_s);
auto time = timer.Get().value();
if (thr.joinable())
thr.join();
EXPECT_NEAR(kSynchronousInterruptTime.value(), time,
kSynchronousInterruptTimeTolerance.value());
}

View File

@@ -1,157 +0,0 @@
// 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.
#include <vector>
#include <gtest/gtest.h>
#include "TestBench.h"
#include "frc/AnalogInput.h"
#include "frc/AnalogOutput.h"
#include "frc/DMA.h"
#include "frc/DMASample.h"
#include "frc/DigitalOutput.h"
#include "frc/Timer.h"
#include "frc/motorcontrol/Jaguar.h"
using namespace frc;
static constexpr auto kDelayTime = 100_ms;
class DMATest : public testing::Test {
protected:
AnalogInput m_analogInput{TestBench::kAnalogOutputChannel};
AnalogOutput m_analogOutput{TestBench::kFakeAnalogOutputChannel};
DigitalOutput m_manualTrigger{TestBench::kLoop1InputChannel};
Jaguar m_pwm{TestBench::kFakePwmOutput};
DMA m_dma;
void SetUp() override {
m_dma.AddAnalogInput(&m_analogInput);
m_dma.SetExternalTrigger(&m_manualTrigger, false, true);
m_manualTrigger.Set(true);
}
};
TEST_F(DMATest, DISABLED_PausingWorks) {
m_dma.Start(1024);
m_dma.SetPause(true);
m_manualTrigger.Set(false);
frc::DMASample sample;
int32_t remaining = 0;
int32_t status = 0;
auto timedOut = sample.Update(&m_dma, 5_ms, &remaining, &status);
ASSERT_EQ(DMASample::DMAReadStatus::kTimeout, timedOut);
}
TEST_F(DMATest, DISABLED_RemovingTriggersWorks) {
m_dma.ClearExternalTriggers();
m_dma.Start(1024);
m_manualTrigger.Set(false);
frc::DMASample sample;
int32_t remaining = 0;
int32_t status = 0;
auto timedOut = sample.Update(&m_dma, 5_ms, &remaining, &status);
ASSERT_EQ(DMASample::DMAReadStatus::kTimeout, timedOut);
}
TEST_F(DMATest, DISABLED_ManualTriggerOnlyHappensOnce) {
m_dma.Start(1024);
m_manualTrigger.Set(false);
frc::DMASample sample;
int32_t remaining = 0;
int32_t status = 0;
auto timedOut = sample.Update(&m_dma, 5_ms, &remaining, &status);
ASSERT_EQ(DMASample::DMAReadStatus::kOk, timedOut);
ASSERT_EQ(0, remaining);
timedOut = sample.Update(&m_dma, 5_ms, &remaining, &status);
ASSERT_EQ(DMASample::DMAReadStatus::kTimeout, timedOut);
}
TEST_F(DMATest, DISABLED_AnalogIndividualTriggers) {
m_dma.Start(1024);
for (double i = 0; i < 5; i += 0.5) {
frc::DMASample sample;
int32_t remaining = 0;
int32_t status = 0;
m_analogOutput.SetVoltage(i);
frc::Wait(kDelayTime);
m_manualTrigger.Set(false);
auto timedOut = sample.Update(&m_dma, 1_ms, &remaining, &status);
m_manualTrigger.Set(true);
ASSERT_EQ(DMASample::DMAReadStatus::kOk, timedOut);
ASSERT_EQ(0, status);
ASSERT_EQ(0, remaining);
ASSERT_DOUBLE_EQ(m_analogInput.GetVoltage(),
sample.GetAnalogInputVoltage(&m_analogInput, &status));
}
}
TEST_F(DMATest, DISABLED_AnalogMultipleTriggers) {
m_dma.Start(1024);
std::vector<double> values;
for (double i = 0; i < 5; i += 0.5) {
values.push_back(i);
m_analogOutput.SetVoltage(i);
frc::Wait(kDelayTime);
m_manualTrigger.Set(false);
frc::Wait(kDelayTime);
m_manualTrigger.Set(true);
}
for (size_t i = 0; i < values.size(); i++) {
frc::DMASample sample;
int32_t remaining = 0;
int32_t status = 0;
auto timedOut = sample.Update(&m_dma, 1_ms, &remaining, &status);
ASSERT_EQ(DMASample::DMAReadStatus::kOk, timedOut);
ASSERT_EQ(0, status);
ASSERT_EQ(values.size() - i - 1, (uint32_t)remaining);
ASSERT_DOUBLE_EQ(values[i],
sample.GetAnalogInputVoltage(&m_analogInput, &status));
}
}
TEST_F(DMATest, DISABLED_TimedTriggers) {
m_dma.SetTimedTrigger(10_ms);
m_dma.Start(1024);
frc::Wait(kDelayTime);
m_dma.SetPause(true);
frc::DMASample sample;
int32_t remaining = 0;
int32_t status = 0;
auto timedOut = sample.Update(&m_dma, 1_ms, &remaining, &status);
ASSERT_EQ(DMASample::DMAReadStatus::kOk, timedOut);
ASSERT_EQ(0, status);
ASSERT_GT(remaining, 5);
}
TEST_F(DMATest, DISABLED_PWMTimedTriggers) {
m_dma.ClearExternalTriggers();
m_dma.SetPwmEdgeTrigger(&m_pwm, true, false);
m_dma.Start(1024);
frc::Wait(kDelayTime);
m_dma.SetPause(true);
frc::DMASample sample;
int32_t remaining = 0;
int32_t status = 0;
auto timedOut = sample.Update(&m_dma, 1_ms, &remaining, &status);
ASSERT_EQ(DMASample::DMAReadStatus::kOk, timedOut);
ASSERT_EQ(0, status);
ASSERT_GT(remaining, 5);
}

View File

@@ -1,59 +0,0 @@
// 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.
#include "frc/DigitalGlitchFilter.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include "frc/Counter.h"
#include "frc/DigitalInput.h"
#include "frc/Encoder.h"
/**
* Tests that configuring inputs to be filtered succeeds.
*
* This test actually tests everything except that the actual FPGA
* implementation works as intended. We configure the FPGA and then query it to
* make sure that the actual configuration matches.
*/
TEST(DigitalGlitchFilterTest, Basic) {
frc::DigitalInput input1{1};
frc::DigitalInput input2{2};
frc::DigitalInput input3{3};
frc::DigitalInput input4{4};
frc::Encoder encoder5{5, 6};
frc::Counter counter7{7};
// Check that we can make a single filter and set the period.
frc::DigitalGlitchFilter filter1;
filter1.Add(&input1);
filter1.SetPeriodNanoSeconds(4200);
// Check that we can make a second filter with 2 inputs.
frc::DigitalGlitchFilter filter2;
filter2.Add(&input2);
filter2.Add(&input3);
filter2.SetPeriodNanoSeconds(97100);
// Check that we can make a third filter with an input, an encoder, and a
// counter.
frc::DigitalGlitchFilter filter3;
filter3.Add(&input4);
filter3.Add(&encoder5);
filter3.Add(&counter7);
filter3.SetPeriodNanoSeconds(167800);
// Verify that the period was properly set for all 3 filters.
EXPECT_EQ(4200u, filter1.GetPeriodNanoSeconds());
EXPECT_EQ(97100u, filter2.GetPeriodNanoSeconds());
EXPECT_EQ(167800u, filter3.GetPeriodNanoSeconds());
// Clean up.
filter1.Remove(&input1);
filter2.Remove(&input2);
filter2.Remove(&input3);
filter3.Remove(&input4);
filter3.Remove(&encoder5);
filter3.Remove(&counter7);
}

View File

@@ -1,37 +0,0 @@
// 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.
#include "frc/DriverStation.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include <hal/DriverStation.h>
#include <units/math.h>
#include <units/time.h>
#include "TestBench.h"
#include "frc/RobotController.h"
#define EXPECT_NEAR_UNITS(val1, val2, eps) \
EXPECT_LE(units::math::abs(val1 - val2), eps)
/**
* Test if the WaitForData function works
*/
TEST(DriverStationTest, WaitForData) {
units::microsecond_t initialTime(frc::RobotController::GetFPGATime());
wpi::Event waitEvent{true};
HAL_ProvideNewDataEventHandle(waitEvent.GetHandle());
// 20ms waiting intervals * 50 = 1s
for (int i = 0; i < 50; i++) {
wpi::WaitForObject(waitEvent.GetHandle());
}
HAL_RemoveNewDataEventHandle(waitEvent.GetHandle());
units::microsecond_t finalTime(frc::RobotController::GetFPGATime());
EXPECT_NEAR_UNITS(1_s, finalTime - initialTime, 200_ms);
}

View File

@@ -1,145 +0,0 @@
// 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.
#include "frc/Encoder.h" // NOLINT(build/include_order)
#include <memory>
#include <gtest/gtest.h>
#include <units/time.h>
#include "TestBench.h"
#include "frc/AnalogOutput.h"
#include "frc/AnalogTrigger.h"
#include "frc/DigitalOutput.h"
#include "frc/Timer.h"
static constexpr auto kDelayTime = 1_ms;
class FakeEncoderTest : public testing::Test {
protected:
frc::DigitalOutput m_outputA{TestBench::kLoop2OutputChannel};
frc::DigitalOutput m_outputB{TestBench::kLoop1OutputChannel};
frc::AnalogOutput m_indexOutput{TestBench::kAnalogOutputChannel};
frc::Encoder m_encoder{TestBench::kLoop1InputChannel,
TestBench::kLoop2InputChannel};
frc::AnalogTrigger m_indexAnalogTrigger{TestBench::kFakeAnalogOutputChannel};
std::shared_ptr<frc::AnalogTriggerOutput> m_indexAnalogTriggerOutput =
m_indexAnalogTrigger.CreateOutput(frc::AnalogTriggerType::kState);
FakeEncoderTest() {
m_outputA.Set(false);
m_outputB.Set(false);
m_indexAnalogTrigger.SetLimitsVoltage(2.0, 3.0);
}
/**
* Output pulses to the encoder's input channels to simulate a change of 100
* ticks
*/
void Simulate100QuadratureTicks() {
for (int32_t i = 0; i < 100; i++) {
m_outputA.Set(true);
frc::Wait(kDelayTime);
m_outputB.Set(true);
frc::Wait(kDelayTime);
m_outputA.Set(false);
frc::Wait(kDelayTime);
m_outputB.Set(false);
frc::Wait(kDelayTime);
}
}
void SetIndexHigh() {
m_indexOutput.SetVoltage(5.0);
frc::Wait(kDelayTime);
}
void SetIndexLow() {
m_indexOutput.SetVoltage(0.0);
frc::Wait(kDelayTime);
}
};
/**
* Test the encoder by resetting it to 0 and reading the value.
*/
TEST_F(FakeEncoderTest, DefaultState) {
EXPECT_DOUBLE_EQ(0.0, m_encoder.Get()) << "The encoder did not start at 0.";
}
/**
* Test the encoder by setting the digital outputs and reading the value.
*/
TEST_F(FakeEncoderTest, CountUp) {
m_encoder.Reset();
Simulate100QuadratureTicks();
EXPECT_DOUBLE_EQ(100.0, m_encoder.Get()) << "Encoder did not count to 100.";
}
/**
* Test that the encoder can stay reset while the index source is high
*/
TEST_F(FakeEncoderTest, ResetWhileHigh) {
m_encoder.SetIndexSource(*m_indexAnalogTriggerOutput,
frc::Encoder::IndexingType::kResetWhileHigh);
SetIndexLow();
Simulate100QuadratureTicks();
SetIndexHigh();
EXPECT_EQ(0, m_encoder.Get());
Simulate100QuadratureTicks();
EXPECT_EQ(0, m_encoder.Get());
}
/**
* Test that the encoder can reset when the index source goes from low to high
*/
TEST_F(FakeEncoderTest, ResetOnRisingEdge) {
m_encoder.SetIndexSource(*m_indexAnalogTriggerOutput,
frc::Encoder::IndexingType::kResetOnRisingEdge);
SetIndexLow();
Simulate100QuadratureTicks();
SetIndexHigh();
EXPECT_EQ(0, m_encoder.Get());
Simulate100QuadratureTicks();
EXPECT_EQ(100, m_encoder.Get());
}
/**
* Test that the encoder can stay reset while the index source is low
*/
TEST_F(FakeEncoderTest, ResetWhileLow) {
m_encoder.SetIndexSource(*m_indexAnalogTriggerOutput,
frc::Encoder::IndexingType::kResetWhileLow);
SetIndexHigh();
Simulate100QuadratureTicks();
SetIndexLow();
EXPECT_EQ(0, m_encoder.Get());
Simulate100QuadratureTicks();
EXPECT_EQ(0, m_encoder.Get());
}
/**
* Test that the encoder can reset when the index source goes from high to low
*/
TEST_F(FakeEncoderTest, ResetOnFallingEdge) {
m_encoder.SetIndexSource(*m_indexAnalogTriggerOutput,
frc::Encoder::IndexingType::kResetOnFallingEdge);
SetIndexHigh();
Simulate100QuadratureTicks();
SetIndexLow();
EXPECT_EQ(0, m_encoder.Get());
Simulate100QuadratureTicks();
EXPECT_EQ(100, m_encoder.Get());
}

View File

@@ -1,10 +0,0 @@
// 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.
#include <gtest/gtest.h>
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -1,204 +0,0 @@
// 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.
#include <algorithm>
#include <gtest/gtest.h>
#include <units/time.h>
#include <wpi/deprecated.h>
#include "TestBench.h"
#include "frc/Encoder.h"
#include "frc/Notifier.h"
#include "frc/Timer.h"
#include "frc/controller/PIDController.h"
#include "frc/filter/LinearFilter.h"
#include "frc/motorcontrol/Jaguar.h"
#include "frc/motorcontrol/Talon.h"
#include "frc/motorcontrol/Victor.h"
enum MotorEncoderTestType { TEST_VICTOR, TEST_JAGUAR, TEST_TALON };
std::ostream& operator<<(std::ostream& os, MotorEncoderTestType const& type) {
switch (type) {
case TEST_VICTOR:
os << "Victor";
break;
case TEST_JAGUAR:
os << "Jaguar";
break;
case TEST_TALON:
os << "Talon";
break;
}
return os;
}
static constexpr auto kMotorTime = 0.5_s;
WPI_IGNORE_DEPRECATED
/**
* A fixture that includes a PWM motor controller and an encoder connected to
* the same motor.
*/
class MotorEncoderTest : public testing::TestWithParam<MotorEncoderTestType> {
protected:
frc::MotorController* m_motorController;
frc::Encoder* m_encoder;
frc::LinearFilter<double>* m_filter;
MotorEncoderTest() {
switch (GetParam()) {
case TEST_VICTOR:
m_motorController = new frc::Victor(TestBench::kVictorChannel);
m_encoder = new frc::Encoder(TestBench::kVictorEncoderChannelA,
TestBench::kVictorEncoderChannelB);
break;
case TEST_JAGUAR:
m_motorController = new frc::Jaguar(TestBench::kJaguarChannel);
m_encoder = new frc::Encoder(TestBench::kJaguarEncoderChannelA,
TestBench::kJaguarEncoderChannelB);
break;
case TEST_TALON:
m_motorController = new frc::Talon(TestBench::kTalonChannel);
m_encoder = new frc::Encoder(TestBench::kTalonEncoderChannelA,
TestBench::kTalonEncoderChannelB);
break;
}
m_filter = new frc::LinearFilter<double>(
frc::LinearFilter<double>::MovingAverage(50));
}
~MotorEncoderTest() {
delete m_filter;
delete m_encoder;
delete m_motorController;
}
void Reset() {
m_motorController->Set(0.0);
m_encoder->Reset();
m_filter->Reset();
}
};
/**
* Test if the encoder value increments after the motor drives forward
*/
TEST_P(MotorEncoderTest, Increment) {
Reset();
/* Drive the motor controller briefly to move the encoder */
m_motorController->Set(0.2f);
frc::Wait(kMotorTime);
m_motorController->Set(0.0);
/* The encoder should be positive now */
EXPECT_GT(m_encoder->Get(), 0)
<< "Encoder should have incremented after the motor moved";
}
/**
* Test if the encoder value decrements after the motor drives backwards
*/
TEST_P(MotorEncoderTest, Decrement) {
Reset();
/* Drive the motor controller briefly to move the encoder */
m_motorController->Set(-0.2);
frc::Wait(kMotorTime);
m_motorController->Set(0.0);
/* The encoder should be positive now */
EXPECT_LT(m_encoder->Get(), 0.0)
<< "Encoder should have decremented after the motor moved";
}
/**
* Test if motor speeds are clamped to [-1,1]
*/
TEST_P(MotorEncoderTest, ClampSpeed) {
Reset();
m_motorController->Set(2.0);
frc::Wait(kMotorTime);
EXPECT_FLOAT_EQ(1.0, m_motorController->Get());
m_motorController->Set(-2.0);
frc::Wait(kMotorTime);
EXPECT_FLOAT_EQ(-1.0, m_motorController->Get());
}
/**
* Test if position PID loop works
*/
TEST_P(MotorEncoderTest, PositionPIDController) {
Reset();
double goal = 1000;
frc::PIDController pidController(0.001, 0.01, 0.0);
pidController.SetTolerance(50.0);
pidController.SetIntegratorRange(-0.2, 0.2);
pidController.SetSetpoint(goal);
/* 10 seconds should be plenty time to get to the reference */
frc::Notifier pidRunner{[this, &pidController] {
auto speed = pidController.Calculate(m_encoder->GetDistance());
m_motorController->Set(std::clamp(speed, -0.2, 0.2));
}};
pidRunner.StartPeriodic(pidController.GetPeriod());
frc::Wait(10_s);
pidRunner.Stop();
RecordProperty("PIDError", pidController.GetPositionError());
EXPECT_TRUE(pidController.AtSetpoint())
<< "PID loop did not converge within 10 seconds. Goal was: " << goal
<< " Error was: " << pidController.GetPositionError();
}
/**
* Test if velocity PID loop works
*/
TEST_P(MotorEncoderTest, VelocityPIDController) {
Reset();
frc::PIDController pidController(1e-5, 0.0, 0.0006);
pidController.SetTolerance(200.0);
pidController.SetSetpoint(600);
/* 10 seconds should be plenty time to get to the reference */
frc::Notifier pidRunner{[this, &pidController] {
auto speed =
pidController.Calculate(m_filter->Calculate(m_encoder->GetRate()));
m_motorController->Set(std::clamp(speed, -0.3, 0.3));
}};
pidRunner.StartPeriodic(pidController.GetPeriod());
frc::Wait(10_s);
pidRunner.Stop();
RecordProperty("PIDError", pidController.GetPositionError());
EXPECT_TRUE(pidController.AtSetpoint())
<< "PID loop did not converge within 10 seconds. Goal was: " << 600
<< " Error was: " << pidController.GetPositionError();
}
/**
* Test resetting encoders
*/
TEST_P(MotorEncoderTest, Reset) {
Reset();
EXPECT_EQ(0, m_encoder->Get()) << "Encoder did not reset to 0";
}
INSTANTIATE_TEST_SUITE_P(Tests, MotorEncoderTest,
testing::Values(TEST_VICTOR, TEST_JAGUAR, TEST_TALON));
WPI_UNIGNORE_DEPRECATED

View File

@@ -1,161 +0,0 @@
// 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.
#include <gtest/gtest.h>
#include <units/time.h>
#include <wpi/deprecated.h>
#include "TestBench.h"
#include "frc/Encoder.h"
#include "frc/Timer.h"
#include "frc/motorcontrol/Jaguar.h"
#include "frc/motorcontrol/Talon.h"
#include "frc/motorcontrol/Victor.h"
enum MotorInvertingTestType { TEST_VICTOR, TEST_JAGUAR, TEST_TALON };
static constexpr double kMotorSpeed = 0.15;
static constexpr auto kDelayTime = 0.5_s;
std::ostream& operator<<(std::ostream& os, MotorInvertingTestType const& type) {
switch (type) {
case TEST_VICTOR:
os << "Victor";
break;
case TEST_JAGUAR:
os << "Jaguar";
break;
case TEST_TALON:
os << "Talon";
break;
}
return os;
}
WPI_IGNORE_DEPRECATED
class MotorInvertingTest
: public testing::TestWithParam<MotorInvertingTestType> {
protected:
frc::MotorController* m_motorController;
frc::Encoder* m_encoder;
MotorInvertingTest() {
switch (GetParam()) {
case TEST_VICTOR:
m_motorController = new frc::Victor(TestBench::kVictorChannel);
m_encoder = new frc::Encoder(TestBench::kVictorEncoderChannelA,
TestBench::kVictorEncoderChannelB);
break;
case TEST_JAGUAR:
m_motorController = new frc::Jaguar(TestBench::kJaguarChannel);
m_encoder = new frc::Encoder(TestBench::kJaguarEncoderChannelA,
TestBench::kJaguarEncoderChannelB);
break;
case TEST_TALON:
m_motorController = new frc::Talon(TestBench::kTalonChannel);
m_encoder = new frc::Encoder(TestBench::kTalonEncoderChannelA,
TestBench::kTalonEncoderChannelB);
break;
}
}
~MotorInvertingTest() {
delete m_encoder;
delete m_motorController;
}
void Reset() {
m_motorController->SetInverted(false);
m_motorController->Set(0.0);
m_encoder->Reset();
}
};
TEST_P(MotorInvertingTest, InvertingPositive) {
Reset();
m_motorController->Set(kMotorSpeed);
frc::Wait(kDelayTime);
bool initDirection = m_encoder->GetDirection();
m_motorController->SetInverted(true);
m_motorController->Set(kMotorSpeed);
frc::Wait(kDelayTime);
EXPECT_TRUE(m_encoder->GetDirection() != initDirection)
<< "Inverting with Positive value does not change direction";
Reset();
}
TEST_P(MotorInvertingTest, InvertingNegative) {
Reset();
m_motorController->SetInverted(false);
m_motorController->Set(-kMotorSpeed);
frc::Wait(kDelayTime);
bool initDirection = m_encoder->GetDirection();
m_motorController->SetInverted(true);
m_motorController->Set(-kMotorSpeed);
frc::Wait(kDelayTime);
EXPECT_TRUE(m_encoder->GetDirection() != initDirection)
<< "Inverting with Negative value does not change direction";
Reset();
}
TEST_P(MotorInvertingTest, InvertingSwitchingPosToNeg) {
Reset();
m_motorController->SetInverted(false);
m_motorController->Set(kMotorSpeed);
frc::Wait(kDelayTime);
bool initDirection = m_encoder->GetDirection();
m_motorController->SetInverted(true);
m_motorController->Set(-kMotorSpeed);
frc::Wait(kDelayTime);
EXPECT_TRUE(m_encoder->GetDirection() == initDirection)
<< "Inverting with Switching value does change direction";
Reset();
}
TEST_P(MotorInvertingTest, InvertingSwitchingNegToPos) {
Reset();
m_motorController->SetInverted(false);
m_motorController->Set(-kMotorSpeed);
frc::Wait(kDelayTime);
bool initDirection = m_encoder->GetDirection();
m_motorController->SetInverted(true);
m_motorController->Set(kMotorSpeed);
frc::Wait(kDelayTime);
EXPECT_TRUE(m_encoder->GetDirection() == initDirection)
<< "Inverting with Switching value does change direction";
Reset();
}
INSTANTIATE_TEST_SUITE_P(Tests, MotorInvertingTest,
testing::Values(TEST_VICTOR, TEST_JAGUAR, TEST_TALON));
WPI_UNIGNORE_DEPRECATED

View File

@@ -1,43 +0,0 @@
// 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.
#include "frc/Notifier.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include <wpi/print.h>
#include "frc/Timer.h"
TEST(NotifierTest, StartPeriodicAndStop) {
uint32_t counter = 0;
frc::Notifier notifier{[&] { ++counter; }};
notifier.StartPeriodic(1_s);
frc::Wait(10.5_s);
notifier.Stop();
EXPECT_EQ(10u, counter) << "Received " << counter
<< " notifications in 10.5 seconds\n";
wpi::print("Received {} notifications in 10.5 seconds\n", counter);
frc::Wait(3_s);
EXPECT_EQ(10u, counter) << "Received " << counter - 10
<< " notifications in 3 seconds\n";
wpi::print("Received {} notifications in 3 seconds\n", counter - 10);
}
TEST(NotifierTest, StartSingle) {
uint32_t counter = 0;
frc::Notifier notifier{[&] { ++counter; }};
notifier.StartSingle(1_s);
frc::Wait(10.5_s);
EXPECT_EQ(1u, counter) << "Received " << counter
<< " notifications in 10.5 seconds\n";
wpi::print("Received {} notifications in 10.5 seconds\n", counter);
}

View File

@@ -1,225 +0,0 @@
// 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.
#include <gtest/gtest.h>
#include "TestBench.h"
#include "frc/AnalogInput.h"
#include "frc/DigitalInput.h"
#include "frc/DigitalOutput.h"
#include "frc/DoubleSolenoid.h"
#include "frc/PneumaticsControlModule.h"
#include "frc/Solenoid.h"
#include "frc/Timer.h"
/* The PCM switches the compressor up to a couple seconds after the pressure
switch changes. */
static constexpr auto kCompressorDelayTime = 3_s;
/* Solenoids should change much more quickly */
static constexpr auto kSolenoidDelayTime = 0.5_s;
/* The voltage divider on the test bench should bring the compressor output
to around these values. */
static const double kCompressorOnVoltage = 5.00;
static const double kCompressorOffVoltage = 1.68;
class PCMTest : public testing::Test {
protected:
frc::PneumaticsControlModule m_pneumaticsModule;
frc::DigitalOutput m_fakePressureSwitch{
TestBench::kFakePressureSwitchChannel};
frc::AnalogInput m_fakeCompressor{TestBench::kFakeCompressorChannel};
frc::DigitalInput m_fakeSolenoid1{TestBench::kFakeSolenoid1Channel};
frc::DigitalInput m_fakeSolenoid2{TestBench::kFakeSolenoid2Channel};
void Reset() {
m_pneumaticsModule.DisableCompressor();
m_fakePressureSwitch.Set(false);
}
};
/**
* Test if the compressor turns on and off when the pressure switch is toggled
*/
TEST_F(PCMTest, PressureSwitch) {
Reset();
m_pneumaticsModule.EnableCompressorDigital();
// Turn on the compressor
m_fakePressureSwitch.Set(true);
frc::Wait(kCompressorDelayTime);
EXPECT_NEAR(kCompressorOnVoltage, m_fakeCompressor.GetVoltage(), 0.5)
<< "Compressor did not turn on when the pressure switch turned on.";
// Turn off the compressor
m_fakePressureSwitch.Set(false);
frc::Wait(kCompressorDelayTime);
EXPECT_NEAR(kCompressorOffVoltage, m_fakeCompressor.GetVoltage(), 0.5)
<< "Compressor did not turn off when the pressure switch turned off.";
}
/**
* Test if the correct solenoids turn on and off when they should
*/
TEST_F(PCMTest, Solenoid) {
Reset();
frc::Solenoid solenoid1{frc::PneumaticsModuleType::CTREPCM,
TestBench::kSolenoidChannel1};
frc::Solenoid solenoid2{frc::PneumaticsModuleType::CTREPCM,
TestBench::kSolenoidChannel2};
// Turn both solenoids off
solenoid1.Set(false);
solenoid2.Set(false);
frc::Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Turn one solenoid on and one off
solenoid1.Set(true);
solenoid2.Set(false);
frc::Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn on";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_TRUE(solenoid1.Get()) << "Solenoid #1 did not read on";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Turn one solenoid on and one off
solenoid1.Set(false);
solenoid2.Set(true);
frc::Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_FALSE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn on";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
// Turn both on
solenoid1.Set(true);
solenoid2.Set(true);
frc::Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn on";
EXPECT_FALSE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn on";
EXPECT_TRUE(solenoid1.Get()) << "Solenoid #1 did not read on";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
}
/**
* Test if the correct solenoids turn on and off when they should when used
* with the DoubleSolenoid class.
*/
TEST_F(PCMTest, DoubleSolenoid) {
frc::DoubleSolenoid solenoid{frc::PneumaticsModuleType::CTREPCM,
TestBench::kSolenoidChannel1,
TestBench::kSolenoidChannel2};
solenoid.Set(frc::DoubleSolenoid::kOff);
frc::Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_TRUE(solenoid.Get() == frc::DoubleSolenoid::kOff)
<< "Solenoid does not read off";
solenoid.Set(frc::DoubleSolenoid::kForward);
frc::Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn on";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_TRUE(solenoid.Get() == frc::DoubleSolenoid::kForward)
<< "Solenoid does not read forward";
solenoid.Set(frc::DoubleSolenoid::kReverse);
frc::Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_FALSE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn on";
EXPECT_TRUE(solenoid.Get() == frc::DoubleSolenoid::kReverse)
<< "Solenoid does not read reverse";
}
TEST_F(PCMTest, OneShot) {
Reset();
frc::Solenoid solenoid1{frc::PneumaticsModuleType::CTREPCM,
TestBench::kSolenoidChannel1};
frc::Solenoid solenoid2{frc::PneumaticsModuleType::CTREPCM,
TestBench::kSolenoidChannel2};
// Turn both solenoids off
solenoid1.Set(false);
solenoid2.Set(false);
frc::Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Pulse Solenoid #1 on, and turn Solenoid #2 off
solenoid1.SetPulseDuration(2 * kSolenoidDelayTime);
solenoid2.Set(false);
solenoid1.StartPulse();
frc::Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn on";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_TRUE(solenoid1.Get()) << "Solenoid #1 did not read on";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
frc::Wait(2 * kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Turn Solenoid #1 off, and pulse Solenoid #2 on
solenoid1.Set(false);
solenoid2.SetPulseDuration(2 * kSolenoidDelayTime);
solenoid2.StartPulse();
frc::Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_FALSE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn on";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
frc::Wait(2 * kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Pulse both Solenoids on
solenoid1.SetPulseDuration(2 * kSolenoidDelayTime);
solenoid2.SetPulseDuration(2 * kSolenoidDelayTime);
solenoid1.StartPulse();
solenoid2.StartPulse();
frc::Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn on";
EXPECT_FALSE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn on";
EXPECT_TRUE(solenoid1.Get()) << "Solenoid #1 did not read on";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
frc::Wait(2 * kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
// Pulse both Solenoids on with different durations
solenoid1.SetPulseDuration(1.5 * kSolenoidDelayTime);
solenoid2.SetPulseDuration(2.5 * kSolenoidDelayTime);
solenoid1.StartPulse();
solenoid2.StartPulse();
frc::Wait(kSolenoidDelayTime);
EXPECT_FALSE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn on";
EXPECT_FALSE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn on";
EXPECT_TRUE(solenoid1.Get()) << "Solenoid #1 did not read on";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
frc::Wait(kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_FALSE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn on";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_TRUE(solenoid2.Get()) << "Solenoid #2 did not read on";
frc::Wait(2 * kSolenoidDelayTime);
EXPECT_TRUE(m_fakeSolenoid1.Get()) << "Solenoid #1 did not turn off";
EXPECT_TRUE(m_fakeSolenoid2.Get()) << "Solenoid #2 did not turn off";
EXPECT_FALSE(solenoid1.Get()) << "Solenoid #1 did not read off";
EXPECT_FALSE(solenoid2.Get()) << "Solenoid #2 did not read off";
}

View File

@@ -1,52 +0,0 @@
// 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.
#include "frc/PowerDistribution.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include <hal/Ports.h>
#include <units/time.h>
#include "TestBench.h"
#include "frc/Timer.h"
#include "frc/motorcontrol/Talon.h"
static constexpr auto kMotorTime = 0.25_s;
class PowerDistributionTest : public testing::Test {
protected:
frc::PowerDistribution m_pdp;
frc::Talon m_talon{TestBench::kTalonChannel};
};
TEST_F(PowerDistributionTest, CheckRepeatedCalls) {
auto numChannels = HAL_GetNumCTREPDPChannels();
// 1 second
for (int i = 0; i < 50; i++) {
for (int j = 0; j < numChannels; j++) {
m_pdp.GetCurrent(j);
}
m_pdp.GetVoltage();
}
frc::Wait(20_ms);
}
/**
* Test if the current changes when the motor is driven using a talon
*/
TEST_F(PowerDistributionTest, CheckCurrentTalon) {
frc::Wait(kMotorTime);
/* The Current should be 0 */
EXPECT_FLOAT_EQ(0, m_pdp.GetCurrent(TestBench::kTalonPDPChannel))
<< "The Talon current was non-zero";
/* Set the motor to full forward */
m_talon.Set(1.0);
frc::Wait(kMotorTime);
/* The current should now be positive */
ASSERT_GT(m_pdp.GetCurrent(TestBench::kTalonPDPChannel), 0)
<< "The Talon current was not positive";
}

View File

@@ -1,163 +0,0 @@
// 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.
#include "frc/Preferences.h" // NOLINT(build/include_order)
#include <cstdio>
#include <fstream>
#include <string>
#include <gtest/gtest.h>
#include <networktables/MultiSubscriber.h>
#include <networktables/NetworkTableInstance.h>
#include <ntcore.h>
#include <units/time.h>
#include "frc/Timer.h"
static const char* kFileName = "networktables.json";
static constexpr auto kSaveTime = 1.2_s;
/**
* If we write a new networktables.ini with some sample values, test that
* we get those same values back using the Preference class.
*/
TEST(PreferencesTest, ReadPreferencesFromFile) {
auto inst = nt::NetworkTableInstance::GetDefault();
inst.StopServer();
std::remove(kFileName);
std::ofstream preferencesFile(kFileName);
preferencesFile << "[" << std::endl;
preferencesFile << "{\"type\":\"string\","
<< "\"name\":\"/Preferences/testFileGetString\","
<< "\"value\":\"Hello, preferences file\","
<< "\"properties\":{\"persistent\":true}}," << std::endl;
preferencesFile << "{\"type\":\"int\","
<< "\"name\":\"/Preferences/testFileGetInt\","
<< "\"value\":1," << "\"properties\":{\"persistent\":true}},"
<< std::endl;
preferencesFile << "{\"type\":\"double\","
<< "\"name\":\"/Preferences/testFileGetDouble\","
<< "\"value\":0.5,"
<< "\"properties\":{\"persistent\":true}}," << std::endl;
preferencesFile << "{\"type\":\"float\","
<< "\"name\":\"/Preferences/testFileGetFloat\","
<< "\"value\":0.25,"
<< "\"properties\":{\"persistent\":true}}," << std::endl;
preferencesFile << "{\"type\":\"boolean\","
<< "\"name\":\"/Preferences/testFileGetBoolean\","
<< "\"value\":true,"
<< "\"properties\":{\"persistent\":true}}]" << std::endl;
preferencesFile.close();
nt::MultiSubscriber suball{inst, {{std::string_view{}}}};
inst.StartServer();
int count = 0;
while ((inst.GetNetworkMode() & NT_NET_MODE_STARTING) != 0) {
frc::Wait(10_ms);
count++;
if (count > 30) {
FAIL() << "timed out waiting for server startup";
}
}
EXPECT_EQ("Hello, preferences file",
frc::Preferences::GetString("testFileGetString"));
EXPECT_EQ(1, frc::Preferences::GetInt("testFileGetInt"));
EXPECT_FLOAT_EQ(0.5, frc::Preferences::GetDouble("testFileGetDouble"));
EXPECT_FLOAT_EQ(0.25f, frc::Preferences::GetFloat("testFileGetFloat"));
EXPECT_TRUE(frc::Preferences::GetBoolean("testFileGetBoolean"));
}
/**
* If we set some values using the Preferences class, test that they show up
* in networktables.json
*/
TEST(PreferencesTest, WritePreferencesToFile) {
auto inst = nt::NetworkTableInstance::GetDefault();
inst.StartServer();
int count = 0;
while ((inst.GetNetworkMode() & NT_NET_MODE_STARTING) != 0) {
frc::Wait(10_ms);
count++;
if (count > 30) {
FAIL() << "timed out waiting for server startup";
}
}
frc::Preferences::Remove("testFileGetString");
frc::Preferences::Remove("testFileGetInt");
frc::Preferences::Remove("testFileGetDouble");
frc::Preferences::Remove("testFileGetFloat");
frc::Preferences::Remove("testFileGetBoolean");
frc::Wait(kSaveTime);
frc::Preferences::SetString("testFileSetString", "Hello, preferences file");
frc::Preferences::SetInt("testFileSetInt", 1);
frc::Preferences::SetDouble("testFileSetDouble", 0.5);
frc::Preferences::SetFloat("testFileSetFloat", 0.25f);
frc::Preferences::SetBoolean("testFileSetBoolean", true);
frc::Wait(kSaveTime);
static char const* kExpectedFileContents[] = {
"[",
" {",
" \"name\": \"/Preferences/testFileSetString\",",
" \"type\": \"string\",",
" \"value\": \"Hello, preferences file\",",
" \"properties\": {",
" \"persistent\": true",
" }",
" },",
" {",
" \"name\": \"/Preferences/testFileSetInt\",",
" \"type\": \"int\",",
" \"value\": 1,",
" \"properties\": {",
" \"persistent\": true",
" }",
" },",
" {",
" \"name\": \"/Preferences/testFileSetDouble\",",
" \"type\": \"double\",",
" \"value\": 0.5,",
" \"properties\": {",
" \"persistent\": true",
" }",
" },",
" {",
" \"name\": \"/Preferences/testFileSetFloat\",",
" \"type\": \"float\",",
" \"value\": 0.25,",
" \"properties\": {",
" \"persistent\": true",
" }",
" },",
" {",
" \"name\": \"/Preferences/testFileSetBoolean\",",
" \"type\": \"boolean\",",
" \"value\": true,",
" \"properties\": {",
" \"persistent\": true",
" }",
" }",
"]"};
std::ifstream preferencesFile(kFileName);
for (auto& kExpectedFileContent : kExpectedFileContents) {
ASSERT_FALSE(preferencesFile.eof())
<< "Preferences file prematurely reached EOF";
std::string line;
std::getline(preferencesFile, line);
ASSERT_EQ(kExpectedFileContent, line)
<< "A line in networktables.json was not correct";
}
}

View File

@@ -1,68 +0,0 @@
// 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.
#include <thread>
#include <gtest/gtest.h>
#include <wpi/condition_variable.h>
#include <wpi/mutex.h>
#include "frc/Threads.h"
TEST(PriorityTest, SetThreadPriority) {
bool exited = false;
wpi::condition_variable cond;
wpi::mutex mutex;
std::thread thr{[&] {
std::unique_lock lock{mutex};
cond.wait(lock, [&] { return exited; });
}};
// Non-RT thread has priority of zero
bool isRealTime = false;
EXPECT_EQ(0, frc::GetThreadPriority(thr, &isRealTime));
EXPECT_FALSE(isRealTime);
// Set thread priority to 15
EXPECT_TRUE(frc::SetThreadPriority(thr, true, 15));
// Verify thread was given priority 15
EXPECT_EQ(15, frc::GetThreadPriority(thr, &isRealTime));
EXPECT_TRUE(isRealTime);
// Set thread back to non-RT
EXPECT_TRUE(frc::SetThreadPriority(thr, false, 0));
// Verify thread is now non-RT
EXPECT_EQ(0, frc::GetThreadPriority(thr, &isRealTime));
EXPECT_FALSE(isRealTime);
{
std::scoped_lock lock{mutex};
exited = true;
}
cond.notify_one();
thr.join();
}
TEST(PriorityTest, SetCurrentThreadPriority) {
// Non-RT thread has priority of zero
bool isRealTime = true;
EXPECT_EQ(0, frc::GetCurrentThreadPriority(&isRealTime));
EXPECT_FALSE(isRealTime);
// Set thread priority to 15
EXPECT_TRUE(frc::SetCurrentThreadPriority(true, 15));
// Verify thread was given priority 15
EXPECT_EQ(15, frc::GetCurrentThreadPriority(&isRealTime));
EXPECT_TRUE(isRealTime);
// Set thread back to non-RT
EXPECT_TRUE(frc::SetCurrentThreadPriority(false, 0));
// Verify thread is now non-RT
EXPECT_EQ(0, frc::GetCurrentThreadPriority(&isRealTime));
EXPECT_FALSE(isRealTime);
}

View File

@@ -1,72 +0,0 @@
// 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.
#include "frc/Relay.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include <units/time.h>
#include "TestBench.h"
#include "frc/DigitalInput.h"
#include "frc/Timer.h"
static constexpr auto kDelayTime = 10_ms;
TEST(RelayTest, BothDirections) {
frc::Relay relay{TestBench::kRelayChannel};
frc::DigitalInput forward{TestBench::kFakeRelayForward};
frc::DigitalInput reverse{TestBench::kFakeRelayReverse};
// Set the relay to forward
relay.Set(frc::Relay::kForward);
frc::Wait(kDelayTime);
EXPECT_TRUE(forward.Get()) << "Relay did not set forward";
EXPECT_FALSE(reverse.Get()) << "Relay did not set forward";
EXPECT_EQ(relay.Get(), frc::Relay::kForward);
// Set the relay to reverse
relay.Set(frc::Relay::kReverse);
frc::Wait(kDelayTime);
EXPECT_TRUE(reverse.Get()) << "Relay did not set reverse";
EXPECT_FALSE(forward.Get()) << "Relay did not set reverse";
EXPECT_EQ(relay.Get(), frc::Relay::kReverse);
// Set the relay to off
relay.Set(frc::Relay::kOff);
frc::Wait(kDelayTime);
EXPECT_FALSE(forward.Get()) << "Relay did not set off";
EXPECT_FALSE(reverse.Get()) << "Relay did not set off";
EXPECT_EQ(relay.Get(), frc::Relay::kOff);
// Set the relay to on
relay.Set(frc::Relay::kOn);
frc::Wait(kDelayTime);
EXPECT_TRUE(forward.Get()) << "Relay did not set on";
EXPECT_TRUE(reverse.Get()) << "Relay did not set on";
EXPECT_EQ(relay.Get(), frc::Relay::kOn);
}
TEST(RelayTest, ForwardOnly) {
frc::Relay relay{TestBench::kRelayChannel, frc::Relay::kForwardOnly};
frc::DigitalInput forward{TestBench::kFakeRelayForward};
frc::DigitalInput reverse{TestBench::kFakeRelayReverse};
relay.Set(frc::Relay::kOn);
frc::Wait(kDelayTime);
EXPECT_TRUE(forward.Get()) << "Relay did not set forward";
EXPECT_FALSE(reverse.Get()) << "Relay did not set forward";
EXPECT_EQ(relay.Get(), frc::Relay::kOn);
}
TEST(RelayTest, ReverseOnly) {
frc::Relay relay{TestBench::kRelayChannel, frc::Relay::kReverseOnly};
frc::DigitalInput forward{TestBench::kFakeRelayForward};
frc::DigitalInput reverse{TestBench::kFakeRelayReverse};
relay.Set(frc::Relay::kOn);
frc::Wait(kDelayTime);
EXPECT_FALSE(forward.Get()) << "Relay did not set reverse";
EXPECT_TRUE(reverse.Get()) << "Relay did not set reverse";
EXPECT_EQ(relay.Get(), frc::Relay::kOn);
}

View File

@@ -1,68 +0,0 @@
// 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.
#include <cstdlib>
#include <thread>
#include <gtest/gtest.h>
#include <hal/HAL.h>
#include <wpi/print.h>
#include "frc/DriverStation.h"
#include "frc/livewindow/LiveWindow.h"
#include "mockds/MockDS.h"
using namespace std::chrono_literals;
class TestEnvironment : public testing::Environment {
bool m_alreadySetUp = false;
MockDS m_mockDS;
public:
TestEnvironment() {
// Only set up once. This allows gtest_repeat to be used to automatically
// repeat tests.
if (m_alreadySetUp) {
return;
}
m_alreadySetUp = true;
if (!HAL_Initialize(500, 0)) {
wpi::print(stderr, "FATAL ERROR: HAL could not be initialized\n");
std::exit(-1);
}
m_mockDS.Start();
// This sets up the network communications library to enable the driver
// 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.
HAL_ObserveUserProgramStarting();
frc::LiveWindow::SetEnabled(false);
wpi::print("Started coms\n");
int enableCounter = 0;
frc::DriverStation::RefreshData();
while (!frc::DriverStation::IsEnabled()) {
if (enableCounter > 50) {
// Robot did not enable properly after 5 seconds.
// Force exit
wpi::print(stderr, " Failed to enable. Aborting\n");
std::terminate();
}
std::this_thread::sleep_for(100ms);
wpi::print("Waiting for enable: {}\n", enableCounter++);
frc::DriverStation::RefreshData();
}
}
~TestEnvironment() override { m_mockDS.Stop(); }
};
testing::Environment* const environment =
testing::AddGlobalTestEnvironment(new TestEnvironment);

View File

@@ -1,116 +0,0 @@
// 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.
#include <cmath>
#include <gtest/gtest.h>
#include <units/time.h>
#include "TestBench.h"
#include "frc/ADXL345_SPI.h"
#include "frc/AnalogGyro.h"
#include "frc/Servo.h"
#include "frc/Timer.h"
static constexpr double kTiltSetpoint0 = 0.22;
static constexpr double kTiltSetpoint45 = 0.45;
static constexpr double kTiltSetpoint90 = 0.68;
/**
* A fixture for the camera with two servos and a gyro
*/
class TiltPanCameraTest : public testing::Test {
protected:
frc::Servo m_tilt{TestBench::kCameraTiltChannel};
frc::Servo m_pan{TestBench::kCameraPanChannel};
frc::ADXL345_SPI m_spiAccel{frc::SPI::kOnboardCS1};
TiltPanCameraTest() {
m_tilt.Set(kTiltSetpoint45);
m_pan.SetAngle(0.0);
// Wait for servos to reset
frc::Wait(2_s);
}
/**
* Test if the servo turns 90 degrees and the gyroscope measures this angle
* Note servo on TestBench is not the same type of servo that servo class
* was designed for so setAngle is significantly off. This has been calibrated
* for the servo on the rig.
*/
void GyroTests(frc::AnalogGyro& gyro) {
gyro.SetSensitivity(0.013);
gyro.Reset();
// Accumulator needs a small amount of time to reset before being tested
frc::Wait(1_ms);
// Verify the gyro angle defaults to 0 immediately after being reset
EXPECT_NEAR(0.0, gyro.GetAngle(), 1.0);
// Make sure that the gyro doesn't get jerked when the servo goes to zero.
m_pan.SetAngle(0.0);
frc::Wait(0.5_s);
gyro.Reset();
for (int32_t i = 0; i < 600; i++) {
m_pan.Set(i / 1000.0);
frc::Wait(1_ms);
}
double gyroAngle = gyro.GetAngle();
EXPECT_NEAR(gyroAngle, 90.0, 10.0)
<< "Gyro measured " << gyroAngle
<< " degrees, servo should have turned 90 degrees";
}
};
TEST_F(TiltPanCameraTest, Gyro) {
int cCenter;
double cOffset;
{
frc::AnalogGyro gyro{TestBench::kCameraGyroChannel};
GyroTests(gyro);
// Gets calibrated parameters from previously calibrated gyro, allocates a
// new gyro with the given parameters for center and offset, and re-runs
// tests on the new gyro.
cCenter = gyro.GetCenter();
cOffset = gyro.GetOffset();
}
frc::AnalogGyro gyro{TestBench::kCameraGyroChannel, cCenter, cOffset};
GyroTests(gyro);
}
/**
* Test if the accelerometer measures gravity along the correct axes when the
* camera rotates
*/
TEST_F(TiltPanCameraTest, SPIAccelerometer) {
static constexpr auto kTiltTime = 1_s;
static constexpr double kAccelerometerTolerance = 0.2;
m_tilt.Set(kTiltSetpoint0);
frc::Wait(kTiltTime);
EXPECT_NEAR(-1.0, m_spiAccel.GetX(), kAccelerometerTolerance);
EXPECT_NEAR(0.0, m_spiAccel.GetY(), kAccelerometerTolerance);
EXPECT_NEAR(0.0, m_spiAccel.GetZ(), kAccelerometerTolerance);
m_tilt.Set(kTiltSetpoint45);
frc::Wait(kTiltTime);
EXPECT_NEAR(-std::sqrt(0.5), m_spiAccel.GetX(), kAccelerometerTolerance);
EXPECT_NEAR(0.0, m_spiAccel.GetY(), kAccelerometerTolerance);
EXPECT_NEAR(std::sqrt(0.5), m_spiAccel.GetZ(), kAccelerometerTolerance);
m_tilt.Set(kTiltSetpoint90);
frc::Wait(kTiltTime);
EXPECT_NEAR(0.0, m_spiAccel.GetX(), kAccelerometerTolerance);
EXPECT_NEAR(0.0, m_spiAccel.GetY(), kAccelerometerTolerance);
EXPECT_NEAR(1.0, m_spiAccel.GetZ(), kAccelerometerTolerance);
}

View File

@@ -1,21 +0,0 @@
// 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.
#include "frc/Timer.h" // NOLINT(build/include_order)
#include <gtest/gtest.h>
#include <units/math.h>
#define EXPECT_NEAR_UNITS(val1, val2, eps) \
EXPECT_LE(units::math::abs(val1 - val2), eps)
TEST(TimerTest, Wait) {
auto initialTime = frc::Timer::GetFPGATimestamp();
frc::Wait(500_ms);
auto finalTime = frc::Timer::GetFPGATimestamp();
EXPECT_NEAR_UNITS(500_ms, finalTime - initialTime, 1_ms);
}

View File

@@ -1,86 +0,0 @@
// 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.
#include "MockDS.h"
#include <stdint.h>
#include <string_view>
#include <hal/cpp/fpga_clock.h>
#include <wpi/Logger.h>
#include <wpi/SmallVector.h>
#include <wpi/print.h>
#include <wpinet/UDPClient.h>
static void LoggerFunc(unsigned int level, const char* file, unsigned int line,
const char* msg) {
if (level == 20) {
wpi::print(stderr, "DS: {}\n", msg);
return;
}
std::string_view levelmsg;
if (level >= 50) {
levelmsg = "CRITICAL";
} else if (level >= 40) {
levelmsg = "ERROR";
} else if (level >= 30) {
levelmsg = "WARNING";
} else {
return;
}
wpi::print(stderr, "DS: {}: {} ({}:{})\n", levelmsg, msg, file, line);
}
static void generateEnabledDsPacket(wpi::SmallVectorImpl<uint8_t>& data,
uint16_t sendCount) {
data.clear();
data.push_back(sendCount >> 8);
data.push_back(sendCount);
data.push_back(0x01); // general data tag
data.push_back(0x04); // teleop enabled
data.push_back(0x10); // normal data request
data.push_back(0x00); // red 1 station
}
void MockDS::Start() {
if (m_active) {
return;
}
m_active = true;
m_thread = std::thread([&]() {
wpi::Logger logger(LoggerFunc);
wpi::UDPClient client(logger);
client.start();
auto timeout_time = hal::fpga_clock::now();
int initCount = 0;
uint16_t sendCount = 0;
wpi::SmallVector<uint8_t, 8> data;
while (m_active) {
// Keep 20ms intervals, and increase time to next interval
auto current = hal::fpga_clock::now();
while (timeout_time <= current) {
timeout_time += std::chrono::milliseconds(20);
}
std::this_thread::sleep_until(timeout_time);
generateEnabledDsPacket(data, sendCount++);
// ~10 disabled packets are required to make the robot actually enable
// 1 is definitely not enough.
if (initCount < 10) {
initCount++;
data[3] = 0;
}
client.send(data, "127.0.0.1", 1110);
}
client.shutdown();
});
}
void MockDS::Stop() {
m_active = false;
if (m_thread.joinable()) {
m_thread.join();
}
}

View File

@@ -1,23 +0,0 @@
// 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 <atomic>
#include <thread>
class MockDS {
public:
MockDS() = default;
~MockDS() { Stop(); }
MockDS(const MockDS& other) = delete;
MockDS& operator=(const MockDS& other) = delete;
void Start();
void Stop();
private:
std::thread m_thread;
std::atomic_bool m_active{false};
};

View File

@@ -1,5 +0,0 @@
// 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.
int main() {}

View File

@@ -1,57 +0,0 @@
// 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 <stdint.h>
class TestBench {
public:
/* Analog input channels */
static constexpr uint32_t kCameraGyroChannel = 0;
static constexpr uint32_t kFakeCompressorChannel = 1;
static constexpr uint32_t kFakeAnalogOutputChannel = 2;
/* Analog output channels */
static constexpr uint32_t kAnalogOutputChannel = 0;
/* DIO channels */
static constexpr uint32_t kTalonEncoderChannelA = 0;
static constexpr uint32_t kTalonEncoderChannelB = 1;
static constexpr uint32_t kVictorEncoderChannelA = 2;
static constexpr uint32_t kVictorEncoderChannelB = 3;
static constexpr uint32_t kJaguarEncoderChannelA = 4;
static constexpr uint32_t kJaguarEncoderChannelB = 5;
static constexpr uint32_t kLoop1OutputChannel = 6;
static constexpr uint32_t kLoop1InputChannel = 7;
static constexpr uint32_t kLoop2OutputChannel = 8;
static constexpr uint32_t kLoop2InputChannel = 9;
/* PWM channels */
static constexpr uint32_t kVictorChannel = 1;
static constexpr uint32_t kJaguarChannel = 2;
static constexpr uint32_t kCameraPanChannel = 8;
static constexpr uint32_t kCameraTiltChannel = 9;
/* MXP digital channels */
static constexpr uint32_t kTalonChannel = 10;
static constexpr uint32_t kFakePressureSwitchChannel = 11;
static constexpr uint32_t kFakeSolenoid1Channel = 12;
static constexpr uint32_t kFakeSolenoid2Channel = 13;
static constexpr uint32_t kFakeRelayForward = 18;
static constexpr uint32_t kFakeRelayReverse = 19;
static constexpr uint32_t kFakePwmOutput = 14;
/* Relay channels */
static constexpr uint32_t kRelayChannel = 0;
/* PDP channels */
static constexpr uint32_t kJaguarPDPChannel = 6;
static constexpr uint32_t kVictorPDPChannel = 8;
static constexpr uint32_t kTalonPDPChannel = 10;
/* PCM channels */
static constexpr int32_t kSolenoidChannel1 = 0;
static constexpr int32_t kSolenoidChannel2 = 1;
};

View File

@@ -1,55 +0,0 @@
plugins {
id 'java'
id 'application'
}
ext {
useJava = true
useCpp = false
skipDev = true
}
apply from: "${rootDir}/shared/opencv.gradle"
application {
mainClass = 'edu.wpi.first.wpilibj.test.AntJunitLauncher'
}
apply plugin: 'com.gradleup.shadow'
repositories {
maven {
url = 'https://frcmaven.wpi.edu/artifactory/ex-mvn'
}
}
dependencies {
implementation project(':wpilibj')
implementation project(':wpimath')
implementation project(':hal')
implementation project(':wpiutil')
implementation project(':wpinet')
implementation project(':ntcore')
implementation project(':cscore')
implementation project(':cameraserver')
implementation 'junit:junit:4.13.2'
testImplementation 'org.hamcrest:hamcrest-all:1.3'
implementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4'
implementation 'org.apache.ant:ant:1.10.12'
implementation 'org.apache.ant:ant-junit:1.10.12'
}
build.dependsOn shadowJar
def testOutputFolder = file("${project(':').buildDir}/integrationTestFiles")
task copyWpilibJIntegrationTestJarToOutput(type: Copy) {
destinationDir testOutputFolder
dependsOn shadowJar
inputs.file shadowJar.archiveFile
from(shadowJar) {
into 'java'
}
}
build.dependsOn copyWpilibJIntegrationTestJarToOutput

View File

@@ -1,254 +0,0 @@
// 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.wpilibj;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.After;
import org.junit.Test;
/**
* This class should not be run as a test explicitly. Instead it should be extended by tests that
* use DigitalSource.
*/
public abstract class AbstractInterruptTest extends AbstractComsSetup {
private DigitalSource m_source = null;
private DigitalSource getSource() {
if (m_source == null) {
m_source = giveSource();
}
return m_source;
}
@After
public void interruptTeardown() {
if (m_source != null) {
freeSource();
m_source = null;
}
}
/** Give the sensor source that interrupts can be attached to. */
abstract DigitalSource giveSource();
/** Cleans up the sensor source. This is only called if {@link #giveSource()} is called. */
abstract void freeSource();
/** Perform whatever action is required to set the interrupt high. */
abstract void setInterruptHigh();
/** Perform whatever action is required to set the interrupt low. */
abstract void setInterruptLow();
@Test(timeout = 1000)
public void testSingleInterruptsTriggering() {
// Given
// final InterruptCounter counter = new InterruptCounter();
// TestInterruptHandlerFunction function = new
// TestInterruptHandlerFunction(counter);
AtomicBoolean hasFired = new AtomicBoolean(false);
AtomicInteger counter = new AtomicInteger(0);
AtomicLong interruptFireTime = new AtomicLong();
try (AsynchronousInterrupt interrupt =
new AsynchronousInterrupt(
getSource(),
(a, b) -> {
interruptFireTime.set(RobotController.getFPGATime());
hasFired.set(true);
counter.incrementAndGet();
})) {
interrupt.enable();
setInterruptLow();
Timer.delay(0.01);
final long interruptTriggerTime = RobotController.getFPGATime();
setInterruptHigh();
while (!hasFired.get()) {
Timer.delay(0.005);
}
assertEquals("The interrupt did not fire the expected number of times", 1, counter.get());
final long range = 10000; // in microseconds
assertThat(
"The interrupt did not fire within the expected time period (values in milliseconds)",
interruptFireTime.get(),
both(greaterThan(interruptTriggerTime - range))
.and(lessThan(interruptTriggerTime + range)));
assertThat(
"The readRisingTimestamp() did not return the correct value (values in seconds)",
interrupt.getRisingTimestamp(),
both(greaterThan((interruptTriggerTime - range) / 1e6))
.and(lessThan((interruptTriggerTime + range) / 1e6)));
}
}
@Test(timeout = 2000)
public void testMultipleInterruptsTriggering() {
AtomicBoolean hasFired = new AtomicBoolean(false);
AtomicInteger counter = new AtomicInteger(0);
try (AsynchronousInterrupt interrupt =
new AsynchronousInterrupt(
getSource(),
(a, b) -> {
hasFired.set(true);
counter.incrementAndGet();
})) {
interrupt.enable();
final int fireCount = 50;
for (int i = 0; i < fireCount; i++) {
setInterruptLow();
setInterruptHigh();
// Wait for the interrupt to complete before moving on
while (!hasFired.getAndSet(false)) {
Timer.delay(0.005);
}
}
// Then
assertEquals(
"The interrupt did not fire the expected number of times", fireCount, counter.get());
}
}
/** The timeout length for this test in seconds. */
private static final int synchronousTimeout = 5;
@Test(timeout = (long) (synchronousTimeout * 1e3))
public void testSynchronousInterruptsTriggering() {
try (SynchronousInterrupt interrupt = new SynchronousInterrupt(getSource())) {
final double synchronousDelay = synchronousTimeout / 2.0;
final Runnable runnable =
() -> {
Timer.delay(synchronousDelay);
setInterruptLow();
setInterruptHigh();
};
// When
// Note: the long time value is used because doubles can flip if the robot
// is left running for long enough
final long startTimeStamp = RobotController.getFPGATime();
new Thread(runnable).start();
// Delay for twice as long as the timeout so the test should fail first
interrupt.waitForInterrupt(synchronousTimeout * 2);
final long stopTimeStamp = RobotController.getFPGATime();
// Then
// The test will not have timed out and:
final double interruptRunTime = (stopTimeStamp - startTimeStamp) * 1e-6;
assertEquals(
"The interrupt did not run for the expected amount of time (units in seconds)",
synchronousDelay,
interruptRunTime,
0.1);
}
}
@Test(timeout = (long) (synchronousTimeout * 1e3))
public void testSynchronousInterruptsWaitResultTimeout() {
try (SynchronousInterrupt interrupt = new SynchronousInterrupt(getSource())) {
SynchronousInterrupt.WaitResult result = interrupt.waitForInterrupt(synchronousTimeout / 2);
assertEquals(
"The interrupt did not time out correctly.",
result,
SynchronousInterrupt.WaitResult.kTimeout);
}
}
@Test(timeout = (long) (synchronousTimeout * 1e3))
public void testSynchronousInterruptsWaitResultRisingEdge() {
try (SynchronousInterrupt interrupt = new SynchronousInterrupt(getSource())) {
final double synchronousDelay = synchronousTimeout / 2.0;
final Runnable runnable =
() -> {
Timer.delay(synchronousDelay);
setInterruptLow();
setInterruptHigh();
};
new Thread(runnable).start();
// Delay for twice as long as the timeout so the test should fail first
SynchronousInterrupt.WaitResult result = interrupt.waitForInterrupt(synchronousTimeout * 2);
assertEquals(
"The interrupt did not fire on the rising edge.",
result,
SynchronousInterrupt.WaitResult.kRisingEdge);
}
}
@Test(timeout = (long) (synchronousTimeout * 1e3))
public void testSynchronousInterruptsWaitResultFallingEdge() {
try (SynchronousInterrupt interrupt = new SynchronousInterrupt(getSource())) {
// Given
interrupt.setInterruptEdges(false, true);
final double synchronousDelay = synchronousTimeout / 2.0;
final Runnable runnable =
() -> {
Timer.delay(synchronousDelay);
setInterruptHigh();
setInterruptLow();
};
new Thread(runnable).start();
// Delay for twice as long as the timeout so the test should fail first
SynchronousInterrupt.WaitResult result = interrupt.waitForInterrupt(synchronousTimeout * 2);
assertEquals(
"The interrupt did not fire on the falling edge.",
result,
SynchronousInterrupt.WaitResult.kFallingEdge);
}
}
@Test(timeout = 4000)
public void testDisableStopsInterruptFiring() {
AtomicBoolean interruptComplete = new AtomicBoolean(false);
AtomicInteger counter = new AtomicInteger(0);
try (AsynchronousInterrupt interrupt =
new AsynchronousInterrupt(
getSource(),
(a, b) -> {
interruptComplete.set(true);
counter.incrementAndGet();
})) {
interrupt.enable();
final int fireCount = 50;
for (int i = 0; i < fireCount; i++) {
setInterruptLow();
setInterruptHigh();
// Wait for the interrupt to complete before moving on
while (!interruptComplete.getAndSet(false)) {
Timer.delay(0.005);
}
}
interrupt.disable();
for (int i = 0; i < fireCount; i++) {
setInterruptLow();
setInterruptHigh();
// Just wait because the interrupt should not fire
Timer.delay(0.005);
}
assertEquals(
"The interrupt did not fire the expected number of times", fireCount, counter.get());
}
}
}

View File

@@ -1,188 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.wpilibj.AnalogTriggerOutput.AnalogTriggerType;
import edu.wpi.first.wpilibj.fixtures.AnalogCrossConnectFixture;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.logging.Logger;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
/** Test that covers the {@link AnalogCrossConnectFixture}. */
public class AnalogCrossConnectTest extends AbstractInterruptTest {
private static final Logger logger = Logger.getLogger(AnalogCrossConnectTest.class.getName());
private static AnalogCrossConnectFixture analogIO;
static final double kDelayTime = 0.01;
@Override
protected Logger getClassLogger() {
return logger;
}
@BeforeClass
public static void setUpBeforeClass() {
analogIO = TestBench.getAnalogCrossConnectFixture();
}
@AfterClass
public static void tearDownAfterClass() {
analogIO.teardown();
analogIO = null;
}
@Before
public void setUp() {
analogIO.setup();
}
@Test
public void testAnalogOuput() {
for (int i = 0; i < 50; i++) {
analogIO.getOutput().setVoltage(i / 10.0);
Timer.delay(kDelayTime);
assertEquals(analogIO.getOutput().getVoltage(), analogIO.getInput().getVoltage(), 0.01);
}
}
@Test
public void testAnalogTriggerBelowWindow() {
// Given
AnalogTrigger trigger = new AnalogTrigger(analogIO.getInput());
trigger.setLimitsVoltage(2.0, 3.0);
// When the output voltage is than less the lower limit
analogIO.getOutput().setVoltage(1.0);
Timer.delay(kDelayTime);
// Then the analog trigger is not in the window and the trigger state is off
assertFalse("Analog trigger is in the window (2V, 3V)", trigger.getInWindow());
assertFalse("Analog trigger is on", trigger.getTriggerState());
trigger.close();
}
@Test
public void testAnalogTriggerInWindow() {
// Given
AnalogTrigger trigger = new AnalogTrigger(analogIO.getInput());
trigger.setLimitsVoltage(2.0, 3.0);
// When the output voltage is within the lower and upper limits
analogIO.getOutput().setVoltage(2.5f);
Timer.delay(kDelayTime);
// Then the analog trigger is in the window and the trigger state is off
assertTrue("Analog trigger is not in the window (2V, 3V)", trigger.getInWindow());
assertFalse("Analog trigger is on", trigger.getTriggerState());
trigger.close();
}
@Test
public void testAnalogTriggerAboveWindow() {
// Given
AnalogTrigger trigger = new AnalogTrigger(analogIO.getInput());
trigger.setLimitsVoltage(2.0, 3.0);
// When the output voltage is greater than the upper limit
analogIO.getOutput().setVoltage(4.0);
Timer.delay(kDelayTime);
// Then the analog trigger is not in the window and the trigger state is on
assertFalse("Analog trigger is in the window (2V, 3V)", trigger.getInWindow());
assertTrue("Analog trigger is not on", trigger.getTriggerState());
trigger.close();
}
@Test
public void testAnalogTriggerCounter() {
// Given
AnalogTrigger trigger = new AnalogTrigger(analogIO.getInput());
trigger.setLimitsVoltage(2.0, 3.0);
Counter counter = new Counter(trigger);
// When the analog output is turned low and high 50 times
for (int i = 0; i < 50; i++) {
analogIO.getOutput().setVoltage(1.0);
Timer.delay(kDelayTime);
analogIO.getOutput().setVoltage(4.0);
Timer.delay(kDelayTime);
}
// Then the counter should be at 50
assertEquals("Analog trigger counter did not count 50 ticks", 50, counter.get());
counter.close();
}
@Test(expected = RuntimeException.class)
public void testRuntimeExceptionOnInvalidAccumulatorPort() {
analogIO.getInput().getAccumulatorCount();
}
private AnalogTrigger m_interruptTrigger;
private AnalogTriggerOutput m_interruptTriggerOutput;
/*
* (non-Javadoc)
*
* @see
* edu.wpi.first.wpilibj.AbstractInterruptTest#giveSource()
*/
@Override
DigitalSource giveSource() {
m_interruptTrigger = new AnalogTrigger(analogIO.getInput());
m_interruptTrigger.setLimitsVoltage(2.0, 3.0);
m_interruptTriggerOutput =
new AnalogTriggerOutput(m_interruptTrigger, AnalogTriggerType.kState);
return m_interruptTriggerOutput;
}
/*
* (non-Javadoc)
*
* @see
* edu.wpi.first.wpilibj.AbstractInterruptTest#freeSource()
*/
@Override
void freeSource() {
m_interruptTriggerOutput.close();
m_interruptTriggerOutput = null;
m_interruptTrigger.close();
m_interruptTrigger = null;
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#setInterruptHigh()
*/
@Override
void setInterruptHigh() {
analogIO.getOutput().setVoltage(4.0);
Timer.delay(kDelayTime);
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#setInterruptLow()
*/
@Override
void setInterruptLow() {
analogIO.getOutput().setVoltage(1.0);
Timer.delay(kDelayTime);
}
}

View File

@@ -1,60 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.fixtures.AnalogCrossConnectFixture;
import edu.wpi.first.wpilibj.mockhardware.FakePotentiometerSource;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/** Tests the {@link AnalogPotentiometer}. */
public class AnalogPotentiometerTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(AnalogPotentiometerTest.class.getName());
private AnalogCrossConnectFixture m_analogIO;
private FakePotentiometerSource m_potSource;
private AnalogPotentiometer m_pot;
private static final double DOUBLE_COMPARISON_DELTA = 2.0;
@Before
public void setUp() {
m_analogIO = TestBench.getAnalogCrossConnectFixture();
m_potSource = new FakePotentiometerSource(m_analogIO.getOutput(), 360);
m_pot = new AnalogPotentiometer(m_analogIO.getInput(), 360.0, 0);
}
@After
public void tearDown() {
m_potSource.reset();
m_pot.close();
m_analogIO.teardown();
}
@Override
protected Logger getClassLogger() {
return logger;
}
@Test
public void testInitialSettings() {
assertEquals(0, m_pot.get(), DOUBLE_COMPARISON_DELTA);
}
@Test
public void testRangeValues() {
for (double i = 0.0; i < 360.0; i = i + 1.0) {
m_potSource.setAngle(i);
m_potSource.setMaxVoltage(RobotController.getVoltage5V());
Timer.delay(0.02);
assertEquals(i, m_pot.get(), DOUBLE_COMPARISON_DELTA);
}
}
}

View File

@@ -1,64 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class BuiltInAccelerometerTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(BuiltInAccelerometerTest.class.getName());
private static final double kAccelerationTolerance = 0.1;
private final BuiltInAccelerometer m_accelerometer;
public BuiltInAccelerometerTest(BuiltInAccelerometer.Range range) {
m_accelerometer = new BuiltInAccelerometer(range);
}
@BeforeClass
public static void waitASecond() {
/*
* The testbench sometimes shakes a little from a previous test. Give it
* some time.
*/
Timer.delay(1.0);
}
/** Test with all valid ranges to make sure unpacking is always done correctly. */
@Parameters
public static Collection<BuiltInAccelerometer.Range[]> generateData() {
return List.of(
new BuiltInAccelerometer.Range[][] {
{BuiltInAccelerometer.Range.k2G},
{BuiltInAccelerometer.Range.k4G},
{BuiltInAccelerometer.Range.k8G}
});
}
@Override
protected Logger getClassLogger() {
return logger;
}
/**
* There's not much we can automatically test with the on-board accelerometer, but checking for
* gravity is probably good enough to tell that it's working.
*/
@Test
public void testAccelerometer() {
assertEquals(0.0, m_accelerometer.getX(), kAccelerationTolerance);
assertEquals(1.0, m_accelerometer.getY(), kAccelerationTolerance);
assertEquals(0.0, m_accelerometer.getZ(), kAccelerationTolerance);
}
}

View File

@@ -1,51 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.logging.Logger;
import org.junit.Test;
/** Tests for checking our constant and port values. */
public class ConstantsPortsTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(ConstantsPortsTest.class.getName());
@Override
protected Logger getClassLogger() {
return logger;
}
/** kDigitalChannels. */
@Test
public void testDigitalChannels() {
assertEquals(31, SensorUtil.kDigitalChannels);
}
/** kAnalogInputChannels. */
@Test
public void testAnalogInputChannels() {
assertEquals(8, SensorUtil.kAnalogInputChannels);
}
/** kAnalogOutputChannels. */
@Test
public void testAnalogOutputChannels() {
assertEquals(2, SensorUtil.kAnalogOutputChannels);
}
/** kPwmChannels. */
@Test
public void testPwmChannels() {
assertEquals(20, SensorUtil.kPwmChannels);
}
/** kRelayChannels. */
@Test
public void testRelayChannels() {
assertEquals(4, SensorUtil.kRelayChannels);
}
}

View File

@@ -1,108 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.wpilibj.fixtures.FakeCounterFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.Collection;
import java.util.logging.Logger;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/** Tests to see if the Counter is working properly. */
@RunWith(Parameterized.class)
public class CounterTest extends AbstractComsSetup {
private static FakeCounterFixture counter = null;
private static final Logger logger = Logger.getLogger(CounterTest.class.getName());
int m_input;
int m_output;
@Override
protected Logger getClassLogger() {
return logger;
}
/**
* Constructs a Counter Test with the given inputs.
*
* @param input The input Port
* @param output The output Port
*/
public CounterTest(int input, int output) {
m_input = input;
m_output = output;
// System.out.println("Counter Test: Input: " + input + " Output: " +
// output);
if (counter != null) {
counter.teardown();
}
counter = new FakeCounterFixture(input, output);
}
/**
* Test data generator. This method is called the the JUnit parameterized test runner and returns
* a Collection of Arrays. For each Array in the Collection, each array element corresponds to a
* parameter in the constructor.
*/
@Parameters
public static Collection<Integer[]> generateData() {
// In this example, the parameter generator returns a List of
// arrays. Each array has two elements: { Digital input port, Digital output
// port}.
// These data are hard-coded into the class, but they could be
// generated or loaded in any way you like.
return TestBench.getDIOCrossConnectCollection();
}
@BeforeClass
public static void setUpBeforeClass() {}
@AfterClass
public static void tearDownAfterClass() {
counter.teardown();
counter = null;
}
@Before
public void setUp() {
counter.setup();
}
/** Tests the default state of the counter immediately after reset. */
@Test
public void testDefault() {
assertEquals("Counter did not reset to 0", 0, counter.getCounter().get());
}
@Test(timeout = 5000)
public void testCount() {
final int goal = 100;
counter.getFakeCounterSource().setCount(goal);
counter.getFakeCounterSource().execute();
final int count = counter.getCounter().get();
assertTrue(
"Fake Counter, Input: "
+ m_input
+ ", Output: "
+ m_output
+ ", did not return "
+ goal
+ " instead got: "
+ count,
count == goal);
}
}

View File

@@ -1,181 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.wpilibj.fixtures.DIOCrossConnectFixture;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.Collection;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/** Tests to see if the Digital ports are working properly. */
@RunWith(Parameterized.class)
public class DIOCrossConnectTest extends AbstractInterruptTest {
private static final Logger logger = Logger.getLogger(DIOCrossConnectTest.class.getName());
private static DIOCrossConnectFixture dio = null;
@Override
protected Logger getClassLogger() {
return logger;
}
/**
* Default constructor for the DIOCrossConnectTest This test is parameterized in order to allow it
* to be tested using a variety of different input/output pairs without duplicate code.<br>
* This class takes Integer port values instead of DigitalClasses because it would force them to
* be instantiated at the same time which could (untested) cause port binding errors.
*
* @param input The port for the input wire
* @param output The port for the output wire
*/
public DIOCrossConnectTest(int input, int output) {
if (dio != null) {
dio.teardown();
}
dio = new DIOCrossConnectFixture(input, output);
}
/**
* Test data generator. This method is called the the JUnit parameterized test runner and returns
* a Collection of Arrays. For each Array in the Collection, each array element corresponds to a
* parameter in the constructor.
*/
@Parameters(name = "{index}: Input Port: {0} Output Port: {1}")
public static Collection<Integer[]> generateData() {
// In this example, the parameter generator returns a List of
// arrays. Each array has two elements: { Digital input port, Digital output
// port}.
// These data are hard-coded into the class, but they could be
// generated or loaded in any way you like.
return TestBench.getDIOCrossConnectCollection();
}
@AfterClass
public static void tearDownAfterClass() {
dio.teardown();
dio = null;
}
@After
public void tearDown() {
dio.reset();
}
/** Tests to see if the DIO can create and recognize a high value. */
@Test
public void testSetHigh() {
dio.getOutput().set(true);
assertTrue("DIO Not High after no delay", dio.getInput().get());
Timer.delay(0.02);
assertTrue("DIO Not High after .05s delay", dio.getInput().get());
}
/** Tests to see if the DIO can create and recognize a low value. */
@Test
public void testSetLow() {
dio.getOutput().set(false);
assertFalse("DIO Not Low after no delay", dio.getInput().get());
Timer.delay(0.02);
assertFalse("DIO Not Low after .05s delay", dio.getInput().get());
}
/** Tests to see if the DIO PWM functionality works. */
@Test
public void testDIOpulseWidthModulation() {
dio.getOutput().set(false);
assertFalse("DIO Not Low after no delay", dio.getInput().get());
// Set frequency to 2.0 Hz
dio.getOutput().setPWMRate(2.0);
// Enable PWM, but leave it off.
dio.getOutput().enablePWM(0.0);
Timer.delay(0.5);
dio.getOutput().updateDutyCycle(0.5);
try (var interruptHandler = new SynchronousInterrupt(dio.getInput())) {
interruptHandler.setInterruptEdges(false, true);
// TODO: Add return value from WaitForInterrupt
interruptHandler.waitForInterrupt(3.0, true);
}
Timer.delay(0.5);
final boolean firstCycle = dio.getInput().get();
Timer.delay(0.5);
final boolean secondCycle = dio.getInput().get();
Timer.delay(0.5);
final boolean thirdCycle = dio.getInput().get();
Timer.delay(0.5);
final boolean forthCycle = dio.getInput().get();
Timer.delay(0.5);
final boolean fifthCycle = dio.getInput().get();
Timer.delay(0.5);
final boolean sixthCycle = dio.getInput().get();
Timer.delay(0.5);
final boolean seventhCycle = dio.getInput().get();
dio.getOutput().disablePWM();
Timer.delay(0.5);
final boolean firstAfterStop = dio.getInput().get();
Timer.delay(0.5);
final boolean secondAfterStop = dio.getInput().get();
assertFalse("DIO Not Low after first delay", firstCycle);
assertTrue("DIO Not High after second delay", secondCycle);
assertFalse("DIO Not Low after third delay", thirdCycle);
assertTrue("DIO Not High after forth delay", forthCycle);
assertFalse("DIO Not Low after fifth delay", fifthCycle);
assertTrue("DIO Not High after sixth delay", sixthCycle);
assertFalse("DIO Not Low after seventh delay", seventhCycle);
assertFalse("DIO Not Low after stopping first read", firstAfterStop);
assertFalse("DIO Not Low after stopping second read", secondAfterStop);
}
/*
* (non-Javadoc)
*
* @see
* edu.wpi.first.wpilibj.AbstractInterruptTest#giveSource()
*/
@Override
DigitalSource giveSource() {
return dio.getInput();
}
/*
* (non-Javadoc)
*
* @see
* edu.wpi.first.wpilibj.AbstractInterruptTest#freeSource()
*/
@Override
void freeSource() {
// Handled in the fixture
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#setInterruptHigh()
*/
@Override
void setInterruptHigh() {
dio.getOutput().set(true);
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#setInterruptLow()
*/
@Override
void setInterruptLow() {
dio.getOutput().set(false);
}
}

View File

@@ -1,161 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.math.util.Units;
import edu.wpi.first.wpilibj.fixtures.AnalogCrossConnectFixture;
import edu.wpi.first.wpilibj.motorcontrol.Jaguar;
import edu.wpi.first.wpilibj.motorcontrol.PWMMotorController;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@Ignore
public class DMATest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(DMATest.class.getName());
@Override
protected Logger getClassLogger() {
return logger;
}
private AnalogCrossConnectFixture m_analogIO;
private DigitalOutput m_manualTrigger;
private PWMMotorController m_pwm;
private DMA m_dma;
private DMASample m_dmaSample;
@Before
public void setUp() {
m_analogIO = TestBench.getAnalogCrossConnectFixture();
m_manualTrigger = new DigitalOutput(7);
m_pwm = new Jaguar(14);
m_dma = new DMA();
m_dmaSample = new DMASample();
m_dma.addAnalogInput(m_analogIO.getInput());
m_dma.setExternalTrigger(m_manualTrigger, false, true);
m_manualTrigger.set(true);
}
@After
public void tearDown() {
m_dma.close();
m_manualTrigger.close();
m_analogIO.teardown();
m_pwm.close();
}
@Test
public void testPausingWorks() {
m_dma.start(1024);
m_dma.setPause(true);
m_manualTrigger.set(false);
var timedOut = m_dmaSample.update(m_dma, Units.millisecondsToSeconds(5));
assertEquals(DMASample.DMAReadStatus.kTimeout, timedOut);
}
@Test
public void testRemovingTriggersWorks() {
m_dma.clearExternalTriggers();
m_dma.start(1024);
m_manualTrigger.set(false);
var timedOut = m_dmaSample.update(m_dma, Units.millisecondsToSeconds(5));
assertEquals(DMASample.DMAReadStatus.kTimeout, timedOut);
}
@Test
public void testManualTriggerOnlyHappensOnce() {
m_dma.start(1024);
m_manualTrigger.set(false);
var timedOut = m_dmaSample.update(m_dma, Units.millisecondsToSeconds(5));
m_manualTrigger.set(true);
assertEquals(DMASample.DMAReadStatus.kOk, timedOut);
assertEquals(0, m_dmaSample.getRemaining());
timedOut = m_dmaSample.update(m_dma, Units.millisecondsToSeconds(5));
assertEquals(DMASample.DMAReadStatus.kTimeout, timedOut);
}
@Test
public void testAnalogIndividualTriggers() {
m_dma.start(1024);
for (double i = 0; i < 5; i += 0.5) {
m_analogIO.getOutput().setVoltage(i);
// Need to sleep to ensure value sets
Timer.delay(AnalogCrossConnectTest.kDelayTime);
m_manualTrigger.set(false);
var timedOut = m_dmaSample.update(m_dma, Units.millisecondsToSeconds(1));
m_manualTrigger.set(true);
assertEquals(DMASample.DMAReadStatus.kOk, timedOut);
assertEquals(0, m_dmaSample.getRemaining());
assertEquals(
m_analogIO.getInput().getVoltage(),
m_dmaSample.getAnalogInputVoltage(m_analogIO.getInput()),
0.01);
}
}
@Test
public void testAnalogMultipleTriggers() {
m_dma.start(1024);
List<Double> values = new ArrayList<>();
for (double i = 0; i < 5; i += 0.5) {
m_analogIO.getOutput().setVoltage(i);
values.add(i);
// Need to sleep to ensure value sets
Timer.delay(AnalogCrossConnectTest.kDelayTime);
m_manualTrigger.set(false);
Timer.delay(AnalogCrossConnectTest.kDelayTime);
m_manualTrigger.set(true);
}
for (int i = 0; i < values.size(); i++) {
var timedOut = m_dmaSample.update(m_dma, Units.millisecondsToSeconds(1));
assertEquals(DMASample.DMAReadStatus.kOk, timedOut);
assertEquals(values.size() - i - 1, m_dmaSample.getRemaining());
assertEquals(values.get(i), m_dmaSample.getAnalogInputVoltage(m_analogIO.getInput()), 0.01);
}
}
@Test
public void testTimedTriggers() {
m_dma.setTimedTrigger(Units.millisecondsToSeconds(10));
m_dma.start(1024);
Timer.delay(Units.millisecondsToSeconds(100));
m_dma.setPause(true);
var timedOut = m_dmaSample.update(m_dma, Units.millisecondsToSeconds(1));
assertEquals(DMASample.DMAReadStatus.kOk, timedOut);
assertTrue("Received more then 5 samples in 100 ms", m_dmaSample.getRemaining() > 5);
}
@Test
public void testPwmTimedTriggers() {
m_dma.clearExternalTriggers();
m_dma.setPwmEdgeTrigger(m_pwm, true, false);
m_dma.start(1024);
Timer.delay(Units.millisecondsToSeconds(100));
m_dma.setPause(true);
var timedOut = m_dmaSample.update(m_dma, Units.millisecondsToSeconds(1));
assertEquals(DMASample.DMAReadStatus.kOk, timedOut);
assertTrue("Received more then 5 samples in 100 ms", m_dmaSample.getRemaining() > 5);
}
}

View File

@@ -1,75 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.logging.Logger;
import org.junit.Test;
/** Test for the DigitalGlitchFilter class. */
public class DigitalGlitchFilterTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(DigitalGlitchFilterTest.class.getName());
@Override
protected Logger getClassLogger() {
return logger;
}
/**
* This is a test to make sure that filters can be created and are consistent. This assumes that
* the FPGA implementation to actually implement the filter has been tested. It does validate that
* we are successfully able to set and get the active filters for ports and makes sure that the
* FPGA filter is changed correctly, and does the same for the period.
*/
@Test
public void testDigitalGlitchFilterBasic() {
final DigitalInput input1 = new DigitalInput(1);
final DigitalInput input2 = new DigitalInput(2);
final DigitalInput input3 = new DigitalInput(3);
final DigitalInput input4 = new DigitalInput(4);
final Encoder encoder5 = new Encoder(5, 6);
final Counter counter7 = new Counter(7);
final DigitalGlitchFilter filter1 = new DigitalGlitchFilter();
filter1.add(input1);
filter1.setPeriodNanoSeconds(4200);
final DigitalGlitchFilter filter2 = new DigitalGlitchFilter();
filter2.add(input2);
filter2.add(input3);
filter2.setPeriodNanoSeconds(97100);
final DigitalGlitchFilter filter3 = new DigitalGlitchFilter();
filter3.add(input4);
filter3.add(encoder5);
filter3.add(counter7);
filter3.setPeriodNanoSeconds(167800);
assertEquals(4200, filter1.getPeriodNanoSeconds());
assertEquals(97100, filter2.getPeriodNanoSeconds());
assertEquals(167800, filter3.getPeriodNanoSeconds());
filter1.remove(input1);
filter2.remove(input2);
filter2.remove(input3);
filter3.remove(input4);
filter3.remove(encoder5);
filter3.remove(counter7);
input1.close();
input2.close();
input3.close();
input4.close();
encoder5.close();
counter7.close();
filter1.close();
filter2.close();
filter3.close();
}
}

View File

@@ -1,52 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.hal.DriverStationJNI;
import edu.wpi.first.util.WPIUtilJNI;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.logging.Logger;
import org.junit.Test;
public class DriverStationTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(TimerTest.class.getName());
private static final double TIMER_TOLERANCE = 0.2;
private static final long TIMER_RUNTIME = 1000000; // 1 second
@Override
protected Logger getClassLogger() {
return logger;
}
@Test
public void waitForDataTest() {
long startTime = RobotController.getFPGATime();
int handle = WPIUtilJNI.createEvent(false, false);
DriverStationJNI.provideNewDataEventHandle(handle);
// Wait for data 50 times
for (int i = 0; i < 50; i++) {
try {
WPIUtilJNI.waitForObject(handle);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long endTime = RobotController.getFPGATime();
long difference = endTime - startTime;
DriverStationJNI.removeNewDataEventHandle(handle);
WPIUtilJNI.destroyEvent(handle);
assertEquals(
"DriverStation waitForData did not wait long enough",
TIMER_RUNTIME,
difference,
TIMER_TOLERANCE * TIMER_RUNTIME);
}
}

View File

@@ -1,141 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.wpilibj.fixtures.FakeEncoderFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.Collection;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/** Test to see if the FPGA properly recognizes a mock Encoder input. */
@RunWith(Parameterized.class)
public class EncoderTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(EncoderTest.class.getName());
private static FakeEncoderFixture encoder = null;
private final boolean m_flip; // Does this test need to flip the inputs
private final int m_inputA;
private final int m_inputB;
private final int m_outputA;
private final int m_outputB;
@Override
protected Logger getClassLogger() {
return logger;
}
/**
* Test data generator. This method is called the the JUnit parametrized test runner and returns a
* Collection of Arrays. For each Array in the Collection, each array element corresponds to a
* parameter in the constructor.
*/
@Parameters
public static Collection<Integer[]> generateData() {
return TestBench.getEncoderDIOCrossConnectCollection();
}
/**
* Constructs a parametrized Encoder Test.
*
* @param inputA The port number for inputA
* @param outputA The port number for outputA
* @param inputB The port number for inputB
* @param outputB The port number for outputB
* @param flip whether or not these set of values require the encoder to be reversed (0 or 1)
*/
public EncoderTest(int inputA, int outputA, int inputB, int outputB, int flip) {
m_inputA = inputA;
m_inputB = inputB;
m_outputA = outputA;
m_outputB = outputB;
// If the encoder from a previous test is allocated then we must free its
// members
if (encoder != null) {
encoder.teardown();
}
m_flip = flip == 0;
encoder = new FakeEncoderFixture(inputA, outputA, inputB, outputB);
}
@AfterClass
public static void tearDownAfterClass() {
encoder.teardown();
encoder = null;
}
/** Sets up the test and verifies that the test was reset to the default state. */
@Before
public void setUp() {
encoder.setup();
testDefaultState();
}
@After
public void tearDown() {
encoder.reset();
}
/** Tests to see if Encoders initialize to zero. */
@Test
public void testDefaultState() {
int value = encoder.getEncoder().get();
assertTrue(errorMessage(0, value), value == 0);
}
/** Tests to see if Encoders can count up successfully. */
@Test
public void testCountUp() {
int goal = 100;
encoder.getFakeEncoderSource().setCount(goal);
encoder.getFakeEncoderSource().setForward(m_flip);
encoder.getFakeEncoderSource().execute();
int value = encoder.getEncoder().get();
assertTrue(errorMessage(goal, value), value == goal);
}
/** Tests to see if Encoders can count down successfully. */
@Test
public void testCountDown() {
int goal = -100;
encoder.getFakeEncoderSource().setCount(goal); // Goal has to be positive
encoder.getFakeEncoderSource().setForward(!m_flip);
encoder.getFakeEncoderSource().execute();
int value = encoder.getEncoder().get();
assertTrue(errorMessage(goal, value), value == goal);
}
/**
* Creates a simple message with the error that was encountered for the Encoders.
*
* @param goal The goal that was trying to be reached
* @param trueValue The actual value that was reached by the test
* @return A fully constructed message with data about where and why the the test failed.
*/
private String errorMessage(int goal, int trueValue) {
return "Encoder ({In,Out}): {"
+ m_inputA
+ ", "
+ m_outputA
+ "},{"
+ m_inputB
+ ", "
+ m_outputB
+ "} Returned: "
+ trueValue
+ ", Wanted: "
+ goal;
}
}

View File

@@ -1,120 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.fixtures.TiltPanCameraFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/** Tests that the {@link TiltPanCameraFixture}. */
public class GyroTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(GyroTest.class.getName());
public static final double TEST_ANGLE = 90.0;
private TiltPanCameraFixture m_tpcam;
@Override
protected Logger getClassLogger() {
return logger;
}
@Before
public void setUp() {
logger.fine("Setup: TiltPan camera");
m_tpcam = TestBench.getTiltPanCam();
m_tpcam.setup();
}
@After
public void tearDown() {
m_tpcam.teardown();
}
@Test
public void testAllGyroTests() {
testInitial(m_tpcam.getGyro());
testDeviationOverTime(m_tpcam.getGyro());
testGyroAngle(m_tpcam.getGyro());
testGyroAngleCalibratedParameters();
}
public void testInitial(AnalogGyro gyro) {
double angle = gyro.getAngle();
assertEquals(errorMessage(angle, 0), 0, angle, 0.5);
}
/**
* Test to see if the Servo and the gyroscope is turning 90 degrees Note servo on TestBench is not
* the same type of servo that servo class was designed for so setAngle is significantly off. This
* has been calibrated for the servo on the rig.
*/
public void testGyroAngle(AnalogGyro gyro) {
// Set angle
for (int i = 0; i < 5; i++) {
m_tpcam.getPan().set(0);
Timer.delay(0.1);
}
Timer.delay(0.5);
// Reset for setup
gyro.reset();
Timer.delay(0.5);
// Perform test
for (int i = 0; i < 53; i++) {
m_tpcam.getPan().set(i / 100.0);
Timer.delay(0.05);
}
Timer.delay(1.2);
double angle = gyro.getAngle();
double difference = TEST_ANGLE - angle;
double diff = Math.abs(difference);
assertEquals(errorMessage(diff, TEST_ANGLE), TEST_ANGLE, angle, 10);
}
protected void testDeviationOverTime(AnalogGyro gyro) {
// Make sure that the test isn't influenced by any previous motions.
Timer.delay(0.5);
gyro.reset();
Timer.delay(0.25);
double angle = gyro.getAngle();
assertEquals(errorMessage(angle, 0), 0, angle, 0.5);
Timer.delay(5);
angle = gyro.getAngle();
assertEquals("After 5 seconds " + errorMessage(angle, 0), 0, angle, 2.0);
}
/**
* Gets calibrated parameters from already calibrated gyro, allocates a new gyro with the center
* and offset parameters, and re-runs the test.
*/
public void testGyroAngleCalibratedParameters() {
// Get calibrated parameters to make new Gyro with parameters
final double calibratedOffset = m_tpcam.getGyro().getOffset();
final int calibratedCenter = m_tpcam.getGyro().getCenter();
m_tpcam.freeGyro();
m_tpcam.setupGyroParam(calibratedCenter, calibratedOffset);
Timer.delay(TiltPanCameraFixture.RESET_TIME);
// Repeat tests
testInitial(m_tpcam.getGyroParam());
testDeviationOverTime(m_tpcam.getGyroParam());
testGyroAngle(m_tpcam.getGyroParam());
}
private String errorMessage(double difference, double target) {
return "Gyro angle skewed " + difference + " deg away from target " + target;
}
}

View File

@@ -1,83 +0,0 @@
// 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.wpilibj;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
public class MockDS {
private Thread m_thread;
private void generateEnabledDsPacket(byte[] data, short sendCount) {
data[0] = (byte) (sendCount >> 8);
data[1] = (byte) sendCount;
data[2] = 0x01; // general data tag
data[3] = 0x04; // teleop enabled
data[4] = 0x10; // normal data request
data[5] = 0x00; // red 1 station
}
/** Start the mock DS thread. */
public void start() {
m_thread =
new Thread(
() -> {
DatagramSocket socket;
try {
socket = new DatagramSocket();
} catch (SocketException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
return;
}
InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 1110);
byte[] sendData = new byte[6];
DatagramPacket packet = new DatagramPacket(sendData, 0, 6, addr);
short sendCount = 0;
int initCount = 0;
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(20);
generateEnabledDsPacket(sendData, sendCount++);
// ~50 disabled packets are required to make the robot actually enable
// 1 is definitely not enough.
if (initCount < 50) {
initCount++;
sendData[3] = 0;
}
packet.setData(sendData);
socket.send(packet);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}
socket.close();
});
// Because of the test setup in Java, this thread will not be stopped
// So it must be a daemon thread
m_thread.setDaemon(true);
m_thread.start();
}
/** Stop the mock DS thread. */
public void stop() {
if (m_thread == null) {
return;
}
m_thread.interrupt();
try {
m_thread.join(1000);
} catch (InterruptedException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}
}

View File

@@ -1,262 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.math.MathUtil;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.filter.LinearFilter;
import edu.wpi.first.wpilibj.fixtures.MotorEncoderFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class MotorEncoderTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(MotorEncoderTest.class.getName());
private static final double MOTOR_RUNTIME = 0.25;
// private static final List<MotorEncoderFixture> pairs = new
// ArrayList<MotorEncoderFixture>();
private static MotorEncoderFixture<?> me = null;
@Override
protected Logger getClassLogger() {
return logger;
}
/**
* Constructs the test.
*
* @param mef The fixture under test.
*/
public MotorEncoderTest(MotorEncoderFixture<?> mef) {
logger.fine("Constructor with: " + mef.getType());
if (me != null && !me.equals(mef)) {
me.teardown();
}
me = mef;
}
@Parameters(name = "{index}: {0}")
public static Collection<MotorEncoderFixture<?>[]> generateData() {
// logger.fine("Loading the MotorList");
return List.of(
new MotorEncoderFixture<?>[][] {
{TestBench.getTalonPair()}, {TestBench.getVictorPair()}, {TestBench.getJaguarPair()}
});
}
@Before
public void setUp() {
double initialSpeed = me.getMotor().get();
assertTrue(
me.getType() + " Did not start with an initial speed of 0 instead got: " + initialSpeed,
Math.abs(initialSpeed) < 0.001);
me.setup();
}
@After
public void tearDown() {
me.reset();
encodersResetCheck(me);
}
@AfterClass
public static void tearDownAfterClass() {
// Clean up the fixture after the test
me.teardown();
me = null;
}
/**
* Test to ensure that the isMotorWithinRange method is functioning properly. Really only needs to
* run on one MotorEncoderFixture to ensure that it is working correctly.
*/
@Test
public void testIsMotorWithinRange() {
double range = 0.01;
assertTrue(me.getType() + " 1", me.isMotorSpeedWithinRange(0.0, range));
assertTrue(me.getType() + " 2", me.isMotorSpeedWithinRange(0.0, -range));
assertFalse(me.getType() + " 3", me.isMotorSpeedWithinRange(1.0, range));
assertFalse(me.getType() + " 4", me.isMotorSpeedWithinRange(-1.0, range));
}
/**
* This test is designed to see if the values of different motors will increment when spun
* forward.
*/
@Test
public void testIncrement() {
int startValue = me.getEncoder().get();
me.getMotor().set(0.2);
Timer.delay(MOTOR_RUNTIME);
int currentValue = me.getEncoder().get();
assertTrue(
me.getType()
+ " Encoder not incremented: start: "
+ startValue
+ "; current: "
+ currentValue,
startValue < currentValue);
}
/**
* This test is designed to see if the values of different motors will decrement when spun in
* reverse.
*/
@Test
public void testDecrement() {
int startValue = me.getEncoder().get();
me.getMotor().set(-0.2);
Timer.delay(MOTOR_RUNTIME);
int currentValue = me.getEncoder().get();
assertTrue(
me.getType()
+ " Encoder not decremented: start: "
+ startValue
+ "; current: "
+ currentValue,
startValue > currentValue);
}
/** This method test if the counters count when the motors rotate. */
@Test
public void testCounter() {
final int counter1Start = me.getCounters()[0].get();
final int counter2Start = me.getCounters()[1].get();
me.getMotor().set(0.75);
Timer.delay(MOTOR_RUNTIME);
int counter1End = me.getCounters()[0].get();
int counter2End = me.getCounters()[1].get();
assertTrue(
me.getType()
+ " Counter not incremented: start: "
+ counter1Start
+ "; current: "
+ counter1End,
counter1Start < counter1End);
assertTrue(
me.getType()
+ " Counter not incremented: start: "
+ counter1Start
+ "; current: "
+ counter2End,
counter2Start < counter2End);
me.reset();
encodersResetCheck(me);
}
/**
* Tests to see if you set the speed to something not {@literal <=} 1.0 if the code appropriately
* throttles the value.
*/
@Test
public void testSetHighForwardSpeed() {
me.getMotor().set(15);
assertTrue(
me.getType() + " Motor speed was not close to 1.0, was: " + me.getMotor().get(),
me.isMotorSpeedWithinRange(1.0, 0.001));
}
/**
* Tests to see if you set the speed to something not {@literal >=} -1.0 if the code appropriately
* throttles the value.
*/
@Test
public void testSetHighReverseSpeed() {
me.getMotor().set(-15);
assertTrue(
me.getType() + " Motor speed was not close to 1.0, was: " + me.getMotor().get(),
me.isMotorSpeedWithinRange(-1.0, 0.001));
}
@Test
public void testPositionPIDController() {
try (PIDController pidController = new PIDController(0.001, 0.0005, 0)) {
pidController.setTolerance(50.0);
pidController.setIntegratorRange(-0.2, 0.2);
pidController.setSetpoint(1000);
try (Notifier pidRunner =
new Notifier(
() -> {
var speed = pidController.calculate(me.getEncoder().getDistance());
me.getMotor().set(MathUtil.clamp(speed, -0.2, 0.2));
})) {
pidRunner.startPeriodic(pidController.getPeriod());
Timer.delay(10.0);
pidRunner.stop();
assertTrue(
"PID loop did not reach reference within 10 seconds. The current error was"
+ pidController.getError(),
pidController.atSetpoint());
}
}
}
@Test
public void testVelocityPIDController() {
LinearFilter filter = LinearFilter.movingAverage(50);
try (PIDController pidController = new PIDController(1e-5, 0.0, 0.0006)) {
pidController.setTolerance(200);
pidController.setSetpoint(600);
try (Notifier pidRunner =
new Notifier(
() -> {
var speed = filter.calculate(me.getEncoder().getRate());
me.getMotor().set(MathUtil.clamp(speed, -0.3, 0.3));
})) {
pidRunner.startPeriodic(pidController.getPeriod());
Timer.delay(10.0);
pidRunner.stop();
assertTrue(
"PID loop did not reach reference within 10 seconds. The error was: "
+ pidController.getError(),
pidController.atSetpoint());
}
}
}
/**
* Checks to see if the encoders and counters are appropriately reset to zero when reset.
*
* @param me The MotorEncoderFixture under test
*/
private void encodersResetCheck(MotorEncoderFixture<?> me) {
assertEquals(
me.getType() + " Encoder value was incorrect after reset.", me.getEncoder().get(), 0);
assertEquals(
me.getType() + " Motor value was incorrect after reset.", me.getMotor().get(), 0, 0);
assertEquals(
me.getType() + " Counter1 value was incorrect after reset.", me.getCounters()[0].get(), 0);
assertEquals(
me.getType() + " Counter2 value was incorrect after reset.", me.getCounters()[1].get(), 0);
Timer.delay(0.5); // so this doesn't fail with the 0.5 second default
// timeout on the encoders
assertTrue(
me.getType() + " Encoder.getStopped() returned false after the motor was reset.",
me.getEncoder().getStopped());
}
}

View File

@@ -1,132 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.wpilibj.fixtures.MotorEncoderFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/** Tests Inversion of motors using the MotorController setInverted. */
@RunWith(Parameterized.class)
public class MotorInvertingTest extends AbstractComsSetup {
static MotorEncoderFixture<?> fixture = null;
private static final double motorspeed = 0.2;
private static final double delaytime = 0.3;
/**
* Constructs the test.
*
* @param afixture The fixture under test.
*/
public MotorInvertingTest(MotorEncoderFixture<?> afixture) {
logger.fine("Constructor with: " + afixture.getType());
if (fixture != null && !fixture.equals(afixture)) {
fixture.teardown();
}
fixture = afixture;
fixture.setup();
}
@Parameters(name = "{index}: {0}")
public static Collection<MotorEncoderFixture<?>[]> generateData() {
// logger.fine("Loading the MotorList");
return List.of(
new MotorEncoderFixture<?>[][] {
{TestBench.getTalonPair()}, {TestBench.getVictorPair()}, {TestBench.getJaguarPair()}
});
}
private static final Logger logger = Logger.getLogger(MotorInvertingTest.class.getName());
@Override
protected Logger getClassLogger() {
return logger;
}
@Before
public void setUp() {
// Reset the fixture elements before every test
fixture.reset();
}
@AfterClass
public static void tearDown() {
fixture.getMotor().setInverted(false);
// Clean up the fixture after the test
fixture.teardown();
}
@Test
public void testInvertingPositive() {
fixture.getMotor().setInverted(false);
fixture.getMotor().set(motorspeed);
Timer.delay(delaytime);
final boolean initDirection = fixture.getEncoder().getDirection();
fixture.getMotor().setInverted(true);
fixture.getMotor().set(motorspeed);
Timer.delay(delaytime);
assertFalse(
"Inverting with Positive value does not change direction",
fixture.getEncoder().getDirection() == initDirection);
fixture.getMotor().set(0);
}
@Test
public void testInvertingNegative() {
fixture.getMotor().setInverted(false);
fixture.getMotor().set(-motorspeed);
Timer.delay(delaytime);
final boolean initDirection = fixture.getEncoder().getDirection();
fixture.getMotor().setInverted(true);
fixture.getMotor().set(-motorspeed);
Timer.delay(delaytime);
assertFalse(
"Inverting with Negative value does not change direction",
fixture.getEncoder().getDirection() == initDirection);
fixture.getMotor().set(0);
}
@Test
public void testInvertingSwitchingPosToNeg() {
fixture.getMotor().setInverted(false);
fixture.getMotor().set(motorspeed);
Timer.delay(delaytime);
final boolean initDirection = fixture.getEncoder().getDirection();
fixture.getMotor().setInverted(true);
fixture.getMotor().set(-motorspeed);
Timer.delay(delaytime);
assertTrue(
"Inverting with Switching value does change direction",
fixture.getEncoder().getDirection() == initDirection);
fixture.getMotor().set(0);
}
@Test
public void testInvertingSwitchingNegToPos() {
fixture.getMotor().setInverted(false);
fixture.getMotor().set(-motorspeed);
Timer.delay(delaytime);
final boolean initDirection = fixture.getEncoder().getDirection();
fixture.getMotor().setInverted(true);
fixture.getMotor().set(motorspeed);
Timer.delay(delaytime);
assertTrue(
"Inverting with Switching value does change direction",
fixture.getEncoder().getDirection() == initDirection);
fixture.getMotor().set(0);
}
}

View File

@@ -1,56 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.logging.Logger;
import org.junit.Test;
/** Tests to see if the Notifier is working properly. */
public class NotifierTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(NotifierTest.class.getName());
private static int counter = 0;
@Override
protected Logger getClassLogger() {
return logger;
}
@Test
public void testStartPeriodicAndStop() {
counter = 0;
Notifier notifier = new Notifier(() -> ++counter);
notifier.startPeriodic(1.0);
Timer.delay(10.5);
notifier.stop();
assertEquals("Received " + counter + " notifications in 10.5 seconds\n", 10, counter);
System.out.println("Received " + counter + " notifications in 10.5 seconds");
Timer.delay(3.0);
assertEquals("Received " + (counter - 10) + " notifications in 3 seconds\n", 10, counter);
System.out.println("Received " + (counter - 10) + " notifications in 3 seconds");
notifier.close();
}
@Test
public void testStartSingle() {
counter = 0;
Notifier notifier = new Notifier(() -> ++counter);
notifier.startSingle(1.0);
Timer.delay(10.5);
assertEquals("Received " + counter + " notifications in 10.5 seconds\n", 1, counter);
System.out.println("Received " + counter + " notifications in 10.5 seconds");
notifier.close();
}
}

View File

@@ -1,266 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.logging.Logger;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
/** Test that covers the {@link Compressor}. */
public class PCMTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(PCMTest.class.getName());
/*
* The PCM switches the compressor up to 2 seconds after the pressure switch
* changes.
*/
protected static final double kCompressorDelayTime = 2.0;
/* Solenoids should change much more quickly */
protected static final double kSolenoidDelayTime = 1.0;
/*
* The voltage divider on the test bench should bring the compressor output to
* around these values.
*/
protected static final double kCompressorOnVoltage = 5.00;
protected static final double kCompressorOffVoltage = 1.68;
private static PneumaticsControlModule pcm;
private static DigitalOutput fakePressureSwitch;
private static AnalogInput fakeCompressor;
private static DigitalInput fakeSolenoid1;
private static DigitalInput fakeSolenoid2;
@BeforeClass
public static void setUpBeforeClass() {
pcm = new PneumaticsControlModule();
fakePressureSwitch = new DigitalOutput(11);
fakeCompressor = new AnalogInput(1);
fakeSolenoid1 = new DigitalInput(12);
fakeSolenoid2 = new DigitalInput(13);
}
@AfterClass
public static void tearDownAfterClass() {
fakePressureSwitch.close();
fakeCompressor.close();
fakeSolenoid1.close();
fakeSolenoid2.close();
}
@Before
public void reset() {
pcm.disableCompressor();
fakePressureSwitch.set(false);
}
/** Test if the compressor turns on and off when the pressure switch is toggled. */
@Test
public void testPressureSwitch() {
final double range = 0.5;
reset();
pcm.enableCompressorDigital();
// Turn on the compressor
fakePressureSwitch.set(true);
Timer.delay(kCompressorDelayTime);
assertEquals(
"Compressor did not turn on when the pressure switch turned on.",
kCompressorOnVoltage,
fakeCompressor.getVoltage(),
range);
// Turn off the compressor
fakePressureSwitch.set(false);
Timer.delay(kCompressorDelayTime);
assertEquals(
"Compressor did not turn off when the pressure switch turned off.",
kCompressorOffVoltage,
fakeCompressor.getVoltage(),
range);
}
/** Test if the correct solenoids turn on and off when they should. */
@Test
public void testSolenoid() {
reset();
Solenoid solenoid1 = new Solenoid(PneumaticsModuleType.CTREPCM, 0);
Solenoid solenoid2 = new Solenoid(PneumaticsModuleType.CTREPCM, 1);
solenoid1.set(false);
solenoid2.set(false);
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Turn Solenoid #1 on, and turn Solenoid #2 off
solenoid1.set(true);
solenoid2.set(false);
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertTrue("Solenoid #1 did not report on", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Turn Solenoid #1 off, and turn Solenoid #2 on
solenoid1.set(false);
solenoid2.set(true);
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
// Turn both Solenoids on
solenoid1.set(true);
solenoid2.set(true);
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertTrue("Solenoid #1 did not report on", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
solenoid1.close();
solenoid2.close();
}
/**
* Test if the correct solenoids turn on and off when they should when used with the
* DoubleSolenoid class.
*/
@Test
public void doubleSolenoid() {
DoubleSolenoid solenoid = new DoubleSolenoid(PneumaticsModuleType.CTREPCM, 0, 1);
solenoid.set(DoubleSolenoid.Value.kOff);
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertTrue("DoubleSolenoid did not report off", solenoid.get() == DoubleSolenoid.Value.kOff);
solenoid.set(DoubleSolenoid.Value.kForward);
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertTrue(
"DoubleSolenoid did not report Forward", solenoid.get() == DoubleSolenoid.Value.kForward);
solenoid.set(DoubleSolenoid.Value.kReverse);
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertTrue(
"DoubleSolenoid did not report Reverse", solenoid.get() == DoubleSolenoid.Value.kReverse);
solenoid.close();
}
/** Test if the correct solenoids turn on and off when they should. */
@Test
public void testOneShot() {
reset();
Solenoid solenoid1 = new Solenoid(PneumaticsModuleType.CTREPCM, 0);
Solenoid solenoid2 = new Solenoid(PneumaticsModuleType.CTREPCM, 1);
solenoid1.set(false);
solenoid2.set(false);
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Pulse Solenoid #1 on, and turn Solenoid #2 off
solenoid1.setPulseDuration(2 * kSolenoidDelayTime);
solenoid1.startPulse();
solenoid2.set(false);
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertTrue("Solenoid #1 did not report on", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
Timer.delay(2 * kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Turn Solenoid #1 off, and pulse Solenoid #2 on
solenoid1.set(false);
solenoid2.setPulseDuration(2 * kSolenoidDelayTime);
solenoid2.startPulse();
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
Timer.delay(2 * kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Pulse both Solenoids on
solenoid1.setPulseDuration(2 * kSolenoidDelayTime);
solenoid2.setPulseDuration(2 * kSolenoidDelayTime);
solenoid1.startPulse();
solenoid2.startPulse();
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertTrue("Solenoid #1 did not report on", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
Timer.delay(2 * kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
// Pulse both Solenoids on with different durations
solenoid1.setPulseDuration(1.5 * kSolenoidDelayTime);
solenoid2.setPulseDuration(2.5 * kSolenoidDelayTime);
solenoid1.startPulse();
solenoid2.startPulse();
Timer.delay(kSolenoidDelayTime);
assertFalse("Solenoid #1 did not turn on", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertTrue("Solenoid #1 did not report on", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertFalse("Solenoid #2 did not turn on", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertTrue("Solenoid #2 did not report on", solenoid2.get());
Timer.delay(kSolenoidDelayTime);
assertTrue("Solenoid #1 did not turn off", fakeSolenoid1.get());
assertTrue("Solenoid #2 did not turn off", fakeSolenoid2.get());
assertFalse("Solenoid #1 did not report off", solenoid1.get());
assertFalse("Solenoid #2 did not report off", solenoid2.get());
solenoid1.close();
solenoid2.close();
}
@Override
protected Logger getClassLogger() {
return logger;
}
}

View File

@@ -1,107 +0,0 @@
// 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.wpilibj;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.hal.can.CANMessageNotFoundException;
import edu.wpi.first.wpilibj.fixtures.MotorEncoderFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/** Test that covers the {@link PowerDistribution}. */
@RunWith(Parameterized.class)
public class PDPTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(PDPTest.class.getName());
private static PowerDistribution pdp;
private static MotorEncoderFixture<?> me;
private final double m_expectedStoppedCurrentDraw;
@BeforeClass
public static void setUpBeforeClass() {
pdp = new PowerDistribution();
}
@AfterClass
public static void tearDownAfterClass() {
pdp = null;
me.teardown();
me = null;
}
/**
* PDPTest constructor.
*
* @param mef Motor encoder fixture.
* @param expectedCurrentDraw Expected current draw in Amps.
*/
public PDPTest(MotorEncoderFixture<?> mef, double expectedCurrentDraw) {
logger.fine("Constructor with: " + mef.getType());
if (me != null && !me.equals(mef)) {
me.teardown();
}
me = mef;
me.setup();
m_expectedStoppedCurrentDraw = expectedCurrentDraw;
}
@Parameters(name = "{index}: {0}, Expected Stopped Current Draw: {1}")
public static Collection<Object[]> generateData() {
// logger.fine("Loading the MotorList");
return List.of(new Object[][] {{TestBench.getTalonPair(), 0.0}});
}
@After
public void tearDown() {
me.reset();
}
/** Test if the current changes when the motor is driven using a talon. */
@Test
public void checkStoppedCurrentForMotorController() throws CANMessageNotFoundException {
Timer.delay(0.25);
/* The Current should be 0 */
assertEquals(
"The low current was not within the expected range.",
m_expectedStoppedCurrentDraw,
pdp.getCurrent(me.getPDPChannel()),
0.001);
}
/** Test if the current changes when the motor is driven using a talon. */
@Test
public void checkRunningCurrentForMotorController() throws CANMessageNotFoundException {
/* Set the motor to full forward */
me.getMotor().set(1.0);
Timer.delay(2);
/* The current should now be greater than the low current */
assertThat(
"The driven current is not greater than the resting current.",
pdp.getCurrent(me.getPDPChannel()),
is(greaterThan(m_expectedStoppedCurrentDraw)));
}
@Override
protected Logger getClassLogger() {
return logger;
}
}

View File

@@ -1,193 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.wpilibj.fixtures.MotorEncoderFixture;
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/** Test that covers the {@link PIDController}. */
@RunWith(Parameterized.class)
public class PIDTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(PIDTest.class.getName());
private NetworkTable m_table;
private SendableBuilderImpl m_builder;
private static final double absoluteTolerance = 50;
private static final double integratorRange = 0.25;
private PIDController m_controller = null;
private static MotorEncoderFixture<?> me = null;
private final double m_p;
private final double m_i;
private final double m_d;
@Override
protected Logger getClassLogger() {
return logger;
}
/**
* PIDTest constructor.
*
* @param p P gain.
* @param i I gain.
* @param d D gain.
* @param mef Motor encoder fixture.
*/
public PIDTest(double p, double i, double d, MotorEncoderFixture<?> mef) {
logger.fine("Constructor with: " + mef.getType());
if (PIDTest.me != null && !PIDTest.me.equals(mef)) {
PIDTest.me.teardown();
}
PIDTest.me = mef;
this.m_p = p;
this.m_i = i;
this.m_d = d;
}
@Parameters
public static Collection<Object[]> generateData() {
// logger.fine("Loading the MotorList");
Collection<Object[]> data = new ArrayList<>();
double kp = 0.001;
double ki = 0.0005;
double kd = 0.0;
for (int i = 0; i < 1; i++) {
data.addAll(
List.of(
new Object[][] {
{kp, ki, kd, TestBench.getTalonPair()},
{kp, ki, kd, TestBench.getVictorPair()},
{kp, ki, kd, TestBench.getJaguarPair()}
}));
}
return data;
}
@BeforeClass
public static void setUpBeforeClass() {}
@AfterClass
public static void tearDownAfterClass() {
logger.fine("TearDownAfterClass: " + me.getType());
me.teardown();
me = null;
}
@Before
public void setUp() {
logger.fine("Setup: " + me.getType());
me.setup();
m_table = NetworkTableInstance.getDefault().getTable("TEST_PID");
m_builder = new SendableBuilderImpl();
m_builder.setTable(m_table);
m_controller = new PIDController(m_p, m_i, m_d);
m_controller.initSendable(m_builder);
}
@After
public void tearDown() {
logger.fine("Teardown: " + me.getType());
m_controller = null;
me.reset();
}
private void setupTolerance() {
m_controller.setTolerance(absoluteTolerance);
}
private void setupIntegratorRange() {
m_controller.setIntegratorRange(-integratorRange, integratorRange);
}
@Test
public void testInitialSettings() {
setupTolerance();
setupIntegratorRange();
double reference = 2500.0;
m_controller.setSetpoint(reference);
assertEquals(
"PID.getPositionError() did not start at " + reference,
reference,
m_controller.getError(),
0);
m_builder.update();
assertEquals(m_p, m_table.getEntry("Kp").getDouble(9999999), 0);
assertEquals(m_i, m_table.getEntry("Ki").getDouble(9999999), 0);
assertEquals(m_d, m_table.getEntry("Kd").getDouble(9999999), 0);
assertEquals(reference, m_table.getEntry("reference").getDouble(9999999), 0);
assertFalse(m_table.getEntry("enabled").getBoolean(true));
}
@Test
public void testSetSetpoint() {
setupTolerance();
setupIntegratorRange();
double reference = 2500.0;
m_controller.setSetpoint(reference);
assertEquals("Did not correctly set reference", reference, m_controller.getSetpoint(), 1e-3);
}
@Test(timeout = 10000)
public void testRotateToTarget() {
setupTolerance();
setupIntegratorRange();
double reference = 1000.0;
assertEquals(pidData() + "did not start at 0", 0, me.getMotor().get(), 0);
m_controller.setSetpoint(reference);
assertEquals(
pidData() + "did not have an error of " + reference, reference, m_controller.getError(), 0);
Notifier pidRunner =
new Notifier(
() -> me.getMotor().set(m_controller.calculate(me.getEncoder().getDistance())));
pidRunner.startPeriodic(m_controller.getPeriod());
Timer.delay(5);
pidRunner.stop();
assertTrue(
pidData() + "Was not on Target. Controller Error: " + m_controller.getError(),
m_controller.atSetpoint());
pidRunner.close();
}
private String pidData() {
return me.getType()
+ " PID {P:"
+ m_controller.getP()
+ " I:"
+ m_controller.getI()
+ " D:"
+ m_controller.getD()
+ "} ";
}
@Test(expected = RuntimeException.class)
public void testOnTargetNoToleranceSet() {
setupIntegratorRange();
assertFalse(m_controller.atSetpoint());
}
}

View File

@@ -1,44 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.logging.Logger;
import org.junit.Test;
/** Tests to see if the thread and process priority functions work. */
public class PriorityTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(PriorityTest.class.getName());
@Override
protected Logger getClassLogger() {
return logger;
}
@Test
public void testSetCurrentThreadPriority() {
// Non-RT thread has priority of zero
assertEquals(0, Threads.getCurrentThreadPriority());
assertFalse(Threads.getCurrentThreadIsRealTime());
// Set thread priority to 15
assertTrue(Threads.setCurrentThreadPriority(true, 15));
// Verify thread was given priority 15
assertEquals(15, Threads.getCurrentThreadPriority());
assertTrue(Threads.getCurrentThreadIsRealTime());
// Set thread back to non-RT
assertTrue(Threads.setCurrentThreadPriority(false, 0));
// Verify thread is now non-RT
assertEquals(0, Threads.getCurrentThreadPriority());
assertFalse(Threads.getCurrentThreadIsRealTime());
}
}

View File

@@ -1,143 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.wpilibj.Relay.Direction;
import edu.wpi.first.wpilibj.Relay.InvalidValueException;
import edu.wpi.first.wpilibj.Relay.Value;
import edu.wpi.first.wpilibj.fixtures.RelayCrossConnectFixture;
import edu.wpi.first.wpilibj.smartdashboard.SendableBuilderImpl;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/** Tests the {@link RelayCrossConnectFixture}. */
public class RelayCrossConnectTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(RelayCrossConnectTest.class.getName());
private static final NetworkTable table =
NetworkTableInstance.getDefault().getTable("_RELAY_CROSS_CONNECT_TEST_");
private RelayCrossConnectFixture m_relayFixture;
private SendableBuilderImpl m_builder;
@Before
public void setUp() {
m_relayFixture = TestBench.getRelayCrossConnectFixture();
m_relayFixture.setup();
m_builder = new SendableBuilderImpl();
m_builder.setTable(table);
m_relayFixture.getRelay().initSendable(m_builder);
}
@After
public void tearDown() {
m_relayFixture.reset();
m_relayFixture.teardown();
}
@Test
public void testBothHigh() {
m_relayFixture.getRelay().setDirection(Direction.kBoth);
m_relayFixture.getRelay().set(Value.kOn);
m_builder.update();
assertTrue(
"Input one was not high when relay set both high", m_relayFixture.getInputOne().get());
assertTrue(
"Input two was not high when relay set both high", m_relayFixture.getInputTwo().get());
assertEquals(Value.kOn, m_relayFixture.getRelay().get());
assertEquals("On", table.getEntry("Value").getString(""));
}
@Test
public void testFirstHigh() {
m_relayFixture.getRelay().setDirection(Direction.kBoth);
m_relayFixture.getRelay().set(Value.kForward);
m_builder.update();
assertFalse(
"Input one was not low when relay set Value.kForward", m_relayFixture.getInputOne().get());
assertTrue(
"Input two was not high when relay set Value.kForward", m_relayFixture.getInputTwo().get());
assertEquals(Value.kForward, m_relayFixture.getRelay().get());
assertEquals("Forward", table.getEntry("Value").getString(""));
}
@Test
public void testSecondHigh() {
m_relayFixture.getRelay().setDirection(Direction.kBoth);
m_relayFixture.getRelay().set(Value.kReverse);
m_builder.update();
assertTrue(
"Input one was not high when relay set Value.kReverse", m_relayFixture.getInputOne().get());
assertFalse(
"Input two was not low when relay set Value.kReverse", m_relayFixture.getInputTwo().get());
assertEquals(Value.kReverse, m_relayFixture.getRelay().get());
assertEquals("Reverse", table.getEntry("Value").getString(""));
}
@Test
public void testForwardDirection() {
m_relayFixture.getRelay().setDirection(Direction.kForward);
m_relayFixture.getRelay().set(Value.kOn);
m_builder.update();
assertFalse(
"Input one was not low when relay set Value.kOn in kForward Direction",
m_relayFixture.getInputOne().get());
assertTrue(
"Input two was not high when relay set Value.kOn in kForward Direction",
m_relayFixture.getInputTwo().get());
assertEquals(Value.kOn, m_relayFixture.getRelay().get());
assertEquals("On", table.getEntry("Value").getString(""));
}
@Test
public void testReverseDirection() {
m_relayFixture.getRelay().setDirection(Direction.kReverse);
m_relayFixture.getRelay().set(Value.kOn);
m_builder.update();
assertTrue(
"Input one was not high when relay set Value.kOn in kReverse Direction",
m_relayFixture.getInputOne().get());
assertFalse(
"Input two was not low when relay set Value.kOn in kReverse Direction",
m_relayFixture.getInputTwo().get());
assertEquals(Value.kOn, m_relayFixture.getRelay().get());
assertEquals("On", table.getEntry("Value").getString(""));
}
@Test(expected = InvalidValueException.class)
public void testSetValueForwardWithDirectionReverseThrowingException() {
m_relayFixture.getRelay().setDirection(Direction.kForward);
m_relayFixture.getRelay().set(Value.kReverse);
}
@Test(expected = InvalidValueException.class)
public void testSetValueReverseWithDirectionForwardThrowingException() {
m_relayFixture.getRelay().setDirection(Direction.kReverse);
m_relayFixture.getRelay().set(Value.kForward);
}
@Test
public void testInitialSettings() {
m_builder.update();
assertEquals(Value.kOff, m_relayFixture.getRelay().get());
// Initially both outputs should be off
assertFalse(m_relayFixture.getInputOne().get());
assertFalse(m_relayFixture.getInputTwo().get());
assertEquals("Off", table.getEntry("Value").getString(""));
}
@Override
protected Logger getClassLogger() {
return logger;
}
}

View File

@@ -1,60 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertTrue;
import edu.wpi.first.wpilibj.fixtures.SampleFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.logging.Logger;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Sample test for a sample PID controller. This demonstrates the general pattern of how to create a
* test and use testing fixtures and categories. All tests must extend from {@link
* AbstractComsSetup} in order to ensure that Network Communications are set up before the tests are
* run.
*/
public class SampleTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(SampleTest.class.getName());
static SampleFixture fixture = new SampleFixture();
@Override
protected Logger getClassLogger() {
return logger;
}
@BeforeClass
public static void classSetup() {
// Set up the fixture before the test is created
fixture.setup();
}
@Before
public void setUp() {
// Reset the fixture elements before every test
fixture.reset();
}
@AfterClass
public static void tearDown() {
// Clean up the fixture after the test
fixture.teardown();
}
/**
* This is just a sample test that asserts true. Any traditional junit code can be used, these are
* ordinary junit tests!
*/
@Test
public void test() {
Timer.delay(0.5);
assertTrue(true);
}
}

View File

@@ -1,41 +0,0 @@
// 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.wpilibj;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import java.util.logging.Logger;
import org.junit.Test;
public class TimerTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(TimerTest.class.getName());
private static final long TIMER_TOLERANCE = (long) (2.5 * 1000);
private static final long TIMER_RUNTIME = 5 * 1000000;
@Override
protected Logger getClassLogger() {
return logger;
}
@Test
public void delayTest() {
// Given
long startTime = RobotController.getTime();
// When
Timer.delay(TIMER_RUNTIME / 1000000);
long endTime = RobotController.getTime();
long difference = endTime - startTime;
// Then
long offset = difference - TIMER_RUNTIME;
assertEquals(
"Timer.delay ran " + offset + " microseconds too long",
TIMER_RUNTIME,
difference,
TIMER_TOLERANCE);
}
}

View File

@@ -1,38 +0,0 @@
// 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.wpilibj;
import edu.wpi.first.wpilibj.test.AbstractTestSuite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
/**
* Holds all of the tests in the root wpilibj directory. Please list alphabetically so that it is
* easy to determine if a test is missing from the list.
*/
@RunWith(Suite.class)
@SuiteClasses({
AnalogCrossConnectTest.class,
AnalogPotentiometerTest.class,
BuiltInAccelerometerTest.class,
ConstantsPortsTest.class,
CounterTest.class,
DigitalGlitchFilterTest.class,
DIOCrossConnectTest.class,
DMATest.class,
DriverStationTest.class,
EncoderTest.class,
GyroTest.class,
MotorEncoderTest.class,
MotorInvertingTest.class,
PCMTest.class,
PDPTest.class,
PIDTest.class,
RelayCrossConnectTest.class,
SampleTest.class,
TimerTest.class
})
public class WpiLibJTestSuite extends AbstractTestSuite {}

View File

@@ -1,18 +0,0 @@
// 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.wpilibj.can;
import edu.wpi.first.hal.can.CANJNI;
import edu.wpi.first.hal.can.CANStatus;
import org.junit.Test;
public class CANStatusTest {
@Test
public void canStatusGetDoesntThrow() {
CANStatus status = new CANStatus();
CANJNI.getCANStatus(status);
// Nothing we can assert, so just make sure it didn't throw.
}
}

View File

@@ -1,73 +0,0 @@
// 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.wpilibj.fixtures;
import edu.wpi.first.wpilibj.AnalogInput;
import edu.wpi.first.wpilibj.AnalogOutput;
/** A fixture that connects an {@link AnalogInput} and an {@link AnalogOutput}. */
public abstract class AnalogCrossConnectFixture implements ITestFixture {
private boolean m_initialized = false;
private AnalogInput m_input;
private AnalogOutput m_output;
protected abstract AnalogInput giveAnalogInput();
protected abstract AnalogOutput giveAnalogOutput();
private void initialize() {
synchronized (this) {
if (!m_initialized) {
m_input = giveAnalogInput();
m_output = giveAnalogOutput();
m_initialized = true;
}
}
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#setup()
*/
@Override
public void setup() {
initialize();
m_output.setVoltage(0);
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#reset()
*/
@Override
public void reset() {
initialize();
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#teardown()
*/
@Override
public void teardown() {
m_input.close();
m_output.close();
}
/** Analog Output. */
public final AnalogOutput getOutput() {
initialize();
return m_output;
}
public final AnalogInput getInput() {
initialize();
return m_input;
}
}

View File

@@ -1,72 +0,0 @@
// 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.wpilibj.fixtures;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.DigitalOutput;
import java.util.logging.Level;
import java.util.logging.Logger;
/** Connects a digital input to a digital output. */
public class DIOCrossConnectFixture implements ITestFixture {
private static final Logger logger = Logger.getLogger(DIOCrossConnectFixture.class.getName());
private final DigitalInput m_input;
private final DigitalOutput m_output;
private boolean m_allocated;
/**
* Constructs using two pre-allocated digital objects.
*
* @param input The input
* @param output The output.
*/
public DIOCrossConnectFixture(DigitalInput input, DigitalOutput output) {
assert input != null;
assert output != null;
m_input = input;
m_output = output;
m_allocated = false;
}
/**
* Constructs a {@link DIOCrossConnectFixture} using the ports of the digital objects.
*
* @param input The port of the {@link DigitalInput}
* @param output The port of the {@link DigitalOutput}
*/
public DIOCrossConnectFixture(int input, int output) {
assert input != output;
m_input = new DigitalInput(input);
m_output = new DigitalOutput(output);
m_allocated = true;
}
public DigitalInput getInput() {
return m_input;
}
public DigitalOutput getOutput() {
return m_output;
}
@Override
public void setup() {}
@Override
public void reset() {
m_output.set(false);
}
@Override
public void teardown() {
logger.log(Level.FINE, "Beginning teardown");
if (m_allocated) {
m_input.close();
m_output.close();
m_allocated = false;
}
}
}

View File

@@ -1,97 +0,0 @@
// 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.wpilibj.fixtures;
import edu.wpi.first.wpilibj.Counter;
import edu.wpi.first.wpilibj.mockhardware.FakeCounterSource;
import java.util.logging.Level;
import java.util.logging.Logger;
/** A fixture that can test the {@link Counter} using a {@link DIOCrossConnectFixture}. */
public class FakeCounterFixture implements ITestFixture {
private static final Logger logger = Logger.getLogger(FakeEncoderFixture.class.getName());
private final DIOCrossConnectFixture m_dio;
private boolean m_allocated;
private final FakeCounterSource m_source;
private final Counter m_counter;
/**
* Constructs a FakeCounterFixture.
*
* @param dio A previously allocated DIOCrossConnectFixture (must be freed outside this class)
*/
public FakeCounterFixture(DIOCrossConnectFixture dio) {
m_dio = dio;
m_allocated = false;
m_source = new FakeCounterSource(dio.getOutput());
m_counter = new Counter(dio.getInput());
}
/**
* Constructs a FakeCounterFixture using two port numbers.
*
* @param input the input port
* @param output the output port
*/
public FakeCounterFixture(int input, int output) {
m_dio = new DIOCrossConnectFixture(input, output);
m_allocated = true;
m_source = new FakeCounterSource(m_dio.getOutput());
m_counter = new Counter(m_dio.getInput());
}
/**
* Retrieves the FakeCouterSource for use.
*
* @return the FakeCounterSource
*/
public FakeCounterSource getFakeCounterSource() {
return m_source;
}
/**
* Gets the Counter for use.
*
* @return the Counter
*/
public Counter getCounter() {
return m_counter;
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#setup()
*/
@Override
public void setup() {}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#reset()
*/
@Override
public void reset() {
m_counter.reset();
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#teardown()
*/
@Override
public void teardown() {
logger.log(Level.FINE, "Beginning teardown");
m_counter.close();
m_source.close();
if (m_allocated) { // Only tear down the dio if this class allocated it
m_dio.teardown();
m_allocated = false;
}
}
}

View File

@@ -1,102 +0,0 @@
// 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.wpilibj.fixtures;
import edu.wpi.first.wpilibj.Encoder;
import edu.wpi.first.wpilibj.mockhardware.FakeEncoderSource;
import java.util.logging.Logger;
/**
* An encoder that uses two {@link DIOCrossConnectFixture DIOCrossConnectFixtures} to test the
* {@link Encoder}.
*/
public class FakeEncoderFixture implements ITestFixture {
private static final Logger logger = Logger.getLogger(FakeEncoderFixture.class.getName());
private final DIOCrossConnectFixture m_dio1;
private final DIOCrossConnectFixture m_dio2;
private boolean m_allocated;
private final FakeEncoderSource m_source;
private int[] m_sourcePort = new int[2];
private final Encoder m_encoder;
private int[] m_encoderPort = new int[2];
/** Constructs a FakeEncoderFixture from two DIOCrossConnectFixture. */
public FakeEncoderFixture(DIOCrossConnectFixture dio1, DIOCrossConnectFixture dio2) {
assert dio1 != null;
assert dio2 != null;
m_dio1 = dio1;
m_dio2 = dio2;
m_allocated = false;
m_source = new FakeEncoderSource(dio1.getOutput(), dio2.getOutput());
m_encoder = new Encoder(dio1.getInput(), dio2.getInput());
}
/** Construcst a FakeEncoderFixture from a set of Digital I/O ports. */
public FakeEncoderFixture(int inputA, int outputA, int inputB, int outputB) {
assert outputA != outputB;
assert outputA != inputA;
assert outputA != inputB;
assert outputB != inputA;
assert outputB != inputB;
assert inputA != inputB;
m_dio1 = new DIOCrossConnectFixture(inputA, outputA);
m_dio2 = new DIOCrossConnectFixture(inputB, outputB);
m_allocated = true;
m_sourcePort[0] = outputA;
m_sourcePort[1] = outputB;
m_encoderPort[0] = inputA;
m_encoderPort[1] = inputB;
m_source = new FakeEncoderSource(m_dio1.getOutput(), m_dio2.getOutput());
m_encoder = new Encoder(m_dio1.getInput(), m_dio2.getInput());
}
public FakeEncoderSource getFakeEncoderSource() {
return m_source;
}
public Encoder getEncoder() {
return m_encoder;
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#setup()
*/
@Override
public void setup() {}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#reset()
*/
@Override
public void reset() {
m_dio1.reset();
m_dio2.reset();
m_encoder.reset();
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#teardown()
*/
@Override
public void teardown() {
logger.fine("Beginning teardown");
m_source.close();
logger.finer("Source freed " + m_sourcePort[0] + ", " + m_sourcePort[1]);
m_encoder.close();
logger.finer("Encoder freed " + m_encoderPort[0] + ", " + m_encoderPort[1]);
if (m_allocated) {
m_dio1.teardown();
m_dio2.teardown();
}
}
}

View File

@@ -1,38 +0,0 @@
// 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.wpilibj.fixtures;
import edu.wpi.first.wpilibj.test.TestBench;
/**
* Common interface for all test fixtures. This ensures that all test fixtures have setup and
* teardown methods, to ensure that the tests run properly. Test fixtures should be modeled around
* the content of a test, rather than the actual physical layout of the testing board. They should
* obtain references to hardware from the {@link TestBench} class, which is a singleton. Testing
* Fixtures are responsible for ensuring that the hardware is in an appropriate state for the start
* of a test, and ensuring that future tests will not be affected by the results of a test.
*/
public interface ITestFixture {
/**
* Performs any required setup for this fixture, ensuring that all fixture elements are ready for
* testing.
*/
void setup();
/**
* Resets the fixture back to test start state. This should be called by the test class in the
* test setup method to ensure that the hardware is in the default state. This differs from {@link
* ITestFixture#setup()} as that is called once, before the class is constructed, so it may need
* to start sensors. This method should not have to start anything, just reset sensors and ensure
* that motors are stopped.
*/
void reset();
/**
* Performs any required teardown after use of the fixture, ensuring that future tests will not
* run into conflicts.
*/
void teardown();
}

View File

@@ -1,201 +0,0 @@
// 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.wpilibj.fixtures;
import edu.wpi.first.wpilibj.Counter;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.Encoder;
import edu.wpi.first.wpilibj.PWM;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.motorcontrol.MotorController;
import edu.wpi.first.wpilibj.test.TestBench;
import java.lang.reflect.ParameterizedType;
import java.util.logging.Logger;
/**
* Represents a physically connected Motor and Encoder to allow for unit tests on these different
* pairs<br>
* Designed to allow the user to easily setup and tear down the fixture to allow for reuse. This
* class should be explicitly instantiated in the TestBed class to allow any test to access this
* fixture. This allows tests to be mailable so that you can easily reconfigure the physical testbed
* without breaking the tests.
*/
@SuppressWarnings("removal")
public abstract class MotorEncoderFixture<T extends MotorController> implements ITestFixture {
private static final Logger logger = Logger.getLogger(MotorEncoderFixture.class.getName());
private boolean m_initialized = false;
private boolean m_tornDown = false;
protected T m_motor;
private Encoder m_encoder;
private final Counter[] m_counters = new Counter[2];
protected DigitalInput m_alphaSource; // Stored so it can be freed at tear down
protected DigitalInput m_betaSource;
/** Default constructor for a MotorEncoderFixture. */
public MotorEncoderFixture() {}
public abstract int getPDPChannel();
/**
* Where the implementer of this class should pass the motor controller Constructor should only be
* called from outside this class if the Motor controller is not also an implementation of PWM
* interface.
*
* @return MotorController
*/
protected abstract T giveMotorController();
/**
* Where the implementer of this class should pass one of the digital inputs.
*
* <p>CONSTRUCTOR SHOULD NOT BE CALLED FROM OUTSIDE THIS CLASS!
*
* @return DigitalInput
*/
protected abstract DigitalInput giveDigitalInputA();
/**
* Where the implementer fo this class should pass the other digital input.
*
* <p>CONSTRUCTOR SHOULD NOT BE CALLED FROM OUTSIDE THIS CLASS!
*
* @return Input B to be used when this class is instantiated
*/
protected abstract DigitalInput giveDigitalInputB();
private void initialize() {
synchronized (this) {
if (!m_initialized) {
m_initialized = true; // This ensures it is only initialized once
m_alphaSource = giveDigitalInputA();
m_betaSource = giveDigitalInputB();
m_encoder = new Encoder(m_alphaSource, m_betaSource);
m_counters[0] = new Counter(m_alphaSource);
m_counters[1] = new Counter(m_betaSource);
logger.fine("Creating the motor controller!");
m_motor = giveMotorController();
}
}
}
@Override
public void setup() {
initialize();
}
/**
* Gets the motor for this Object.
*
* @return the motor this object refers too
*/
public T getMotor() {
initialize();
return m_motor;
}
/**
* Gets the encoder for this object.
*
* @return the encoder that this object refers too
*/
public Encoder getEncoder() {
initialize();
return m_encoder;
}
public Counter[] getCounters() {
initialize();
return m_counters;
}
/**
* Retrieves the name of the motor that this object refers to.
*
* @return The simple name of the motor {@link Class#getSimpleName()}
*/
public String getType() {
initialize();
return m_motor.getClass().getSimpleName();
}
/**
* Checks to see if the speed of the motor is within some range of a given value. This is used
* instead of equals() because doubles can have inaccuracies.
*
* @param value The value to compare against
* @param accuracy The accuracy range to check against to see if the
* @return true if the range of values between motors speed accuracy contains the 'value'.
*/
public boolean isMotorSpeedWithinRange(double value, double accuracy) {
initialize();
return Math.abs(Math.abs(m_motor.get()) - Math.abs(value)) < Math.abs(accuracy);
}
@Override
public void reset() {
initialize();
m_motor.setInverted(false);
m_motor.set(0);
Timer.delay(TestBench.MOTOR_STOP_TIME); // DEFINED IN THE TestBench
m_encoder.reset();
for (Counter c : m_counters) {
c.reset();
}
}
/**
* Safely tears down the MotorEncoder Fixture in a way that makes sure that even if an object
* fails to initialize the rest of the fixture can still be torn down and the resources
* deallocated.
*/
@Override
public void teardown() {
if (!m_tornDown) {
if (m_motor != null) {
if (m_motor instanceof PWM motor) {
motor.close();
}
m_motor = null;
}
if (m_encoder != null) {
m_encoder.close();
m_encoder = null;
}
if (m_counters[0] != null) {
m_counters[0].close();
m_counters[0] = null;
}
if (m_counters[1] != null) {
m_counters[1].close();
m_counters[1] = null;
}
if (m_alphaSource != null) {
m_alphaSource.close();
m_alphaSource = null;
}
if (m_betaSource != null) {
m_betaSource.close();
m_betaSource = null;
}
m_tornDown = true;
}
}
@Override
public String toString() {
StringBuilder string = new StringBuilder("MotorEncoderFixture<");
// Get the generic type as a class
@SuppressWarnings("unchecked")
Class<T> class1 =
(Class<T>)
((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
string.append(class1.getSimpleName());
string.append(">");
return string.toString();
}
}

View File

@@ -1,90 +0,0 @@
// 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.wpilibj.fixtures;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.Relay;
/** A connection between a {@link Relay} and two {@link DigitalInput DigitalInputs}. */
public abstract class RelayCrossConnectFixture implements ITestFixture {
private DigitalInput m_inputOne;
private DigitalInput m_inputTwo;
private Relay m_relay;
private boolean m_initialized = false;
private boolean m_freed = false;
protected abstract Relay giveRelay();
protected abstract DigitalInput giveInputOne();
protected abstract DigitalInput giveInputTwo();
private void initialize() {
synchronized (this) {
if (!m_initialized) {
m_relay = giveRelay();
m_inputOne = giveInputOne();
m_inputTwo = giveInputTwo();
m_initialized = true;
}
}
}
public Relay getRelay() {
initialize();
return m_relay;
}
public DigitalInput getInputOne() {
initialize();
return m_inputOne;
}
public DigitalInput getInputTwo() {
initialize();
return m_inputTwo;
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#setup()
*/
@Override
public void setup() {
initialize();
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#reset()
*/
@Override
public void reset() {
initialize();
}
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.fixtures.ITestFixture#teardown()
*/
@Override
public void teardown() {
if (!m_freed) {
m_relay.close();
m_inputOne.close();
m_inputTwo.close();
m_freed = true;
} else {
throw new RuntimeException(
"You attempted to free the "
+ RelayCrossConnectFixture.class.getSimpleName()
+ " multiple times");
}
}
}

View File

@@ -1,41 +0,0 @@
// 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.wpilibj.fixtures;
/**
* This is an example of how to use the {@link ITestFixture} interface to create test fixtures for a
* test.
*/
public class SampleFixture implements ITestFixture {
@Override
public void setup() {
/*
* If this fixture actually accessed the hardware, here is where it would
* set up the starting state of the test bench. For example, resetting
* encoders, ensuring motors are stopped, resetting any serial communication
* if necessary, etc.
*/
}
@Override
public void reset() {
/*
* This is where the fixture would reset any sensors or motors used by the
* fixture to test default state. This method should not worry about whether
* or not the sensors have been allocated correctly, that is the job of the
* setup function.
*/
}
@Override
public void teardown() {
/*
* This is where the fixture would deallocate and reset back to normal
* conditions any necessary hardware. This includes ensuring motors are
* stopped, stoppable sensors are actually stopped, ensuring serial
* communications are ready for the next test, etc.
*/
}
}

View File

@@ -1,102 +0,0 @@
// 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.wpilibj.fixtures;
import edu.wpi.first.wpilibj.AnalogGyro;
import edu.wpi.first.wpilibj.Servo;
import edu.wpi.first.wpilibj.Timer;
import java.util.logging.Logger;
/**
* A class to represent the a physical Camera with two servos (tilt and pan) designed to test to see
* if the gyroscope is operating normally.
*/
public abstract class TiltPanCameraFixture implements ITestFixture {
public static final Logger logger = Logger.getLogger(TiltPanCameraFixture.class.getName());
public static final double RESET_TIME = 2.0;
private AnalogGyro m_gyro;
private AnalogGyro m_gyroParam;
private Servo m_tilt;
private Servo m_pan;
private boolean m_initialized = false;
protected abstract AnalogGyro giveGyro();
protected abstract AnalogGyro giveGyroParam(int center, double offset);
protected abstract Servo giveTilt();
protected abstract Servo givePan();
/** Constructs the TiltPanCamera. */
public TiltPanCameraFixture() {}
@Override
public void setup() {
if (!m_initialized) {
m_initialized = true;
m_tilt = giveTilt();
m_tilt.set(0);
m_pan = givePan();
m_pan.set(0);
Timer.delay(RESET_TIME);
logger.fine("Initializing the gyro");
m_gyro = giveGyro();
m_gyro.reset();
}
}
@Override
public void reset() {
if (m_gyro != null) {
m_gyro.reset();
}
}
public Servo getTilt() {
return m_tilt;
}
public Servo getPan() {
return m_pan;
}
public AnalogGyro getGyro() {
return m_gyro;
}
public AnalogGyro getGyroParam() {
return m_gyroParam;
}
// Do not call unless all other instances of Gyro have been deallocated
public void setupGyroParam(int center, double offset) {
m_gyroParam = giveGyroParam(center, offset);
m_gyroParam.reset();
}
public void freeGyro() {
m_gyro.close();
m_gyro = null;
}
@Override
public void teardown() {
m_tilt.close();
m_tilt = null;
m_pan.close();
m_pan = null;
if (m_gyro != null) { // in case not freed during gyro tests
m_gyro.close();
m_gyro = null;
}
if (m_gyroParam != null) { // in case gyro tests failed before getting to this point
m_gyroParam.close();
m_gyroParam = null;
}
}
}

View File

@@ -1,121 +0,0 @@
// 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.wpilibj.mockhardware;
import edu.wpi.first.wpilibj.DigitalOutput;
import edu.wpi.first.wpilibj.Timer;
/** Simulates an encoder for testing purposes. */
public class FakeCounterSource implements AutoCloseable {
private Thread m_task;
private int m_count;
private int m_milliSec;
private DigitalOutput m_output;
private boolean m_allocated;
/** Thread object that allows emulation of an encoder. */
private static class EncoderThread extends Thread {
FakeCounterSource m_encoder;
EncoderThread(FakeCounterSource encode) {
m_encoder = encode;
}
@Override
public void run() {
m_encoder.m_output.set(false);
try {
for (int i = 0; i < m_encoder.m_count; i++) {
Thread.sleep(m_encoder.m_milliSec);
m_encoder.m_output.set(true);
Thread.sleep(m_encoder.m_milliSec);
m_encoder.m_output.set(false);
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
/**
* Create a fake encoder on a given port.
*
* @param output the port to output the given signal to
*/
public FakeCounterSource(DigitalOutput output) {
m_output = output;
m_allocated = false;
initEncoder();
}
/**
* Create a fake encoder on a given port.
*
* @param port The port the encoder is supposed to be on
*/
public FakeCounterSource(int port) {
m_output = new DigitalOutput(port);
m_allocated = true;
initEncoder();
}
/** Destroy Object with minimum memory leak. */
@Override
public void close() {
m_task = null;
if (m_allocated) {
m_output.close();
m_output = null;
m_allocated = false;
}
}
/** Common initialization code. */
private void initEncoder() {
m_milliSec = 1;
m_task = new EncoderThread(this);
m_output.set(false);
}
/** Starts the thread execution task. */
public void start() {
m_task.start();
}
/** Waits for the thread to complete. */
public void complete() {
try {
m_task.join();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
m_task = new EncoderThread(this);
Timer.delay(0.01);
}
/** Starts and completes a task set - does not return until thread has finished its operations. */
public void execute() {
start();
complete();
}
/**
* Sets the count to run encoder.
*
* @param count The count to emulate to the controller
*/
public void setCount(int count) {
m_count = count;
}
/**
* Specify the rate to send pulses.
*
* @param milliSec The rate to send out pulses at
*/
public void setRate(int milliSec) {
m_milliSec = milliSec;
}
}

View File

@@ -1,163 +0,0 @@
// 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.wpilibj.mockhardware;
import edu.wpi.first.wpilibj.DigitalOutput;
import edu.wpi.first.wpilibj.Timer;
/** Emulates a quadrature encoder. */
public class FakeEncoderSource implements AutoCloseable {
private Thread m_task;
private int m_count;
private int m_milliSec;
private boolean m_forward;
private final DigitalOutput m_outputA;
private final DigitalOutput m_outputB;
private final boolean m_allocatedOutputs;
/** Thread object that allows emulation of a quadrature encoder. */
private static class QuadEncoderThread extends Thread {
FakeEncoderSource m_encoder;
QuadEncoderThread(FakeEncoderSource encode) {
m_encoder = encode;
}
@Override
public void run() {
final DigitalOutput lead;
final DigitalOutput lag;
m_encoder.m_outputA.set(false);
m_encoder.m_outputB.set(false);
if (m_encoder.isForward()) {
lead = m_encoder.m_outputA;
lag = m_encoder.m_outputB;
} else {
lead = m_encoder.m_outputB;
lag = m_encoder.m_outputA;
}
try {
for (int i = 0; i < m_encoder.m_count; i++) {
lead.set(true);
Thread.sleep(m_encoder.m_milliSec);
lag.set(true);
Thread.sleep(m_encoder.m_milliSec);
lead.set(false);
Thread.sleep(m_encoder.m_milliSec);
lag.set(false);
Thread.sleep(m_encoder.m_milliSec);
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
/**
* Creates an encoder source using two ports.
*
* @param portA The A port
* @param portB The B port
*/
public FakeEncoderSource(int portA, int portB) {
m_outputA = new DigitalOutput(portA);
m_outputB = new DigitalOutput(portB);
m_allocatedOutputs = true;
initQuadEncoder();
}
/**
* Creates the fake encoder using two digital outputs.
*
* @param outputA The A digital output
* @param outputB The B digital output
*/
public FakeEncoderSource(DigitalOutput outputA, DigitalOutput outputB) {
m_outputA = outputA;
m_outputB = outputB;
m_allocatedOutputs = false;
initQuadEncoder();
}
/** Frees the resource. */
@Override
public void close() {
m_task = null;
if (m_allocatedOutputs) {
m_outputA.close();
m_outputB.close();
}
}
/** Common initialization code. */
private void initQuadEncoder() {
m_milliSec = 1;
m_forward = true;
m_task = new QuadEncoderThread(this);
m_outputA.set(false);
m_outputB.set(false);
}
/** Starts the thread. */
public void start() {
m_task.start();
}
/** Waits for thread to end. */
public void complete() {
try {
m_task.join();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
m_task = new QuadEncoderThread(this);
Timer.delay(0.01);
}
/** Runs and waits for thread to end before returning. */
public void execute() {
start();
complete();
}
/**
* Rate of pulses to send.
*
* @param milliSec Pulse Rate
*/
public void setRate(int milliSec) {
m_milliSec = milliSec;
}
/**
* Set the number of pulses to simulate.
*
* @param count Pulse count
*/
public void setCount(int count) {
m_count = Math.abs(count);
}
/**
* Set which direction the encoder simulates motion in.
*
* @param isForward Whether to simulate forward motion
*/
public void setForward(boolean isForward) {
m_forward = isForward;
}
/**
* Accesses whether the encoder is simulating forward motion.
*
* @return Whether the simulated motion is in the forward direction
*/
public boolean isForward() {
return m_forward;
}
}

View File

@@ -1,87 +0,0 @@
// 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.wpilibj.mockhardware;
import edu.wpi.first.wpilibj.AnalogOutput;
/** A fake source to provide output to a {@link edu.wpi.first.wpilibj.interfaces.Potentiometer}. */
public class FakePotentiometerSource implements AutoCloseable {
private AnalogOutput m_output;
private boolean m_initOutput;
private double m_potMaxAngle;
private double m_potMaxVoltage = 5.0;
private final double m_defaultPotMaxAngle;
/**
* Constructs the fake source.
*
* @param output The analog port to output the signal on
* @param defaultPotMaxAngle The default maximum angle the pot supports.
*/
public FakePotentiometerSource(AnalogOutput output, double defaultPotMaxAngle) {
m_defaultPotMaxAngle = defaultPotMaxAngle;
m_potMaxAngle = defaultPotMaxAngle;
m_output = output;
m_initOutput = false;
}
public FakePotentiometerSource(int port, double defaultPotMaxAngle) {
this(new AnalogOutput(port), defaultPotMaxAngle);
m_initOutput = true;
}
/**
* Sets the maximum voltage output. If not the default is 5.0V.
*
* @param voltage The voltage that indicates that the pot is at the max value.
*/
public void setMaxVoltage(double voltage) {
m_potMaxVoltage = voltage;
}
public void setRange(double range) {
m_potMaxAngle = range;
}
public void reset() {
m_potMaxAngle = m_defaultPotMaxAngle;
m_output.setVoltage(0.0);
}
public void setAngle(double angle) {
m_output.setVoltage((m_potMaxVoltage / m_potMaxAngle) * angle);
}
public void setVoltage(double voltage) {
m_output.setVoltage(voltage);
}
public double getVoltage() {
return m_output.getVoltage();
}
/**
* Returns the currently set angle.
*
* @return the current angle
*/
public double getAngle() {
double voltage = m_output.getVoltage();
if (voltage == 0) { // Removes divide by zero error
return 0;
}
return voltage * (m_potMaxAngle / m_potMaxVoltage);
}
/** Frees the resource. */
@Override
public void close() {
if (m_initOutput) {
m_output.close();
m_output = null;
m_initOutput = false;
}
}
}

View File

@@ -1,247 +0,0 @@
// 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.wpilibj.test;
import edu.wpi.first.hal.DriverStationJNI;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.MockDS;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
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;
/**
* 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.
*/
public abstract class AbstractComsSetup {
/** Stores whether network coms have been initialized. */
private static boolean initialized = false;
// We have no way to stop the MockDS, so its thread is daemon.
private static MockDS ds;
/*
* This sets up the network communications library to enable the driver 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) {
try {
// Set some implementations so that the static methods work properly
HAL.initialize(500, 0);
DriverStationJNI.observeUserProgramStarting();
DriverStation.getAlliance();
ds = new MockDS();
ds.start();
LiveWindow.setEnabled(false);
TestBench.out().println("Started coms");
} catch (Exception ex) {
TestBench.out().println("Exception during AbstractComsSetup initialization: " + ex);
ex.printStackTrace(TestBench.out());
throw ex;
}
// Wait until the robot is enabled before starting the tests
int enableCounter = 0;
DriverStation.refreshData();
while (!DriverStation.isEnabled()) {
if (enableCounter > 50) {
// Robot did not enable properly after 5 seconds.
// Force exit
TestBench.err().println("Failed to enable. Aborting");
System.exit(1);
}
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
TestBench.out().println("Waiting for enable: " + enableCounter++);
DriverStation.refreshData();
}
TestBench.out().println();
// Ready to go!
initialized = true;
TestBench.out().println("Running!");
}
}
protected abstract Logger getClassLogger();
/** This causes a stack trace to be printed as the test is running as well as at the end. */
@Rule
public final TestWatcher getTestWatcher() {
return getOverridenTestWatcher();
}
/**
* 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() {
return new DefaultTestWatcher();
}
protected class DefaultTestWatcher extends TestWatcher {
/**
* Allows a failure to supply a custom status message to be displayed along with the stack
* trace.
*/
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 (throwable instanceof MultipleFailureException mfe) {
/*
* 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 exceptionCount = 1; // Running exception count
int failureCount = mfe.getFailures().size();
for (Throwable singleThrown : mfe.getFailures()) {
getClassLogger()
.logp(
Level.SEVERE,
description.getClassName(),
description.getMethodName(),
(exceptionCount++)
+ "/"
+ failureCount
+ " "
+ description.getDisplayName()
+ " "
+ "failed "
+ singleThrown.getMessage()
+ "\n"
+ status,
singleThrown);
}
} else {
getClassLogger()
.logp(
Level.SEVERE,
description.getClassName(),
description.getMethodName(),
description.getDisplayName() + " failed " + throwable.getMessage() + "\n" + status,
throwable);
}
super.failed(throwable, description);
}
/*
* (non-Javadoc)
*
* @see org.junit.rules.TestWatcher#failed(java.lang.Throwable,
* org.junit.runner.Description)
*/
@Override
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 enableCounter = 0;
while (!DriverStation.isEnabled()) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// Prints the message on one line overwriting itself each time
TestBench.out().print("\rWaiting for enable: " + enableCounter++);
}
getClassLogger()
.logp(Level.INFO, description.getClassName(), description.getMethodName(), "Starting");
super.starting(description);
}
@Override
protected void succeeded(Description description) {
simpleLog(Level.INFO, "TEST PASSED!");
super.succeeded(description);
}
}
/**
* Logs a simple message without the logger formatting associated with it.
*
* @param level The level to log the message at
* @param message The message to log
*/
protected void simpleLog(Level level, String message) {
if (getClassLogger().isLoggable(level)) {
TestBench.out().println(message);
}
}
/**
* Provided as a replacement to lambda functions to allow for repeatable checks to see if a
* correct state is reached.
*/
public abstract static class BooleanCheck {
public BooleanCheck() {}
/**
* 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
*/
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
* @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 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 (timeoutIndex = 0;
timeoutIndex < (timeout * 100) && !correctState.getAsBoolean();
timeoutIndex++) {
Timer.delay(0.01);
}
if (correctState.getAsBoolean()) {
simpleLog(level, message + " took " + (timeoutIndex * 0.01) + " seconds");
} else {
simpleLog(level, message + " timed out after " + (timeoutIndex * 0.01) + " seconds");
}
return timeoutIndex * 0.01;
}
}

View File

@@ -1,255 +0,0 @@
// 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.wpilibj.test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
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;
/**
* Allows tests suites and tests to be run selectively from the command line using a regex text
* pattern.
*/
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>.
* @throws RuntimeException If the <code>@SuiteClasses</code> annotation is missing.
*/
protected List<Class<?>> getAnnotatedTestClasses() {
SuiteClasses annotation = getClass().getAnnotation(SuiteClasses.class);
if (annotation == null) {
throw new RuntimeException(
String.format("class '%s' must have a SuiteClasses annotation", getClass().getName()));
}
return List.of(annotation.value());
}
private boolean areAnySuperClassesOfTypeAbstractTestSuite(Class<?> check) {
while (check != null) {
if (check.equals(AbstractTestSuite.class)) {
return true;
}
check = check.getSuperclass();
}
return false;
}
/**
* Stores a method name and method class pair. Used when searching for methods matching a given
* regex text.
*/
protected static class ClassMethodPair {
public final Class<?> m_methodClass;
public final String m_methodName;
public ClassMethodPair(Class<?> klass, Method method) {
m_methodClass = klass;
m_methodName = method.getName();
}
public Request getMethodRunRequest() {
return Request.method(m_methodClass, m_methodName);
}
}
protected List<ClassMethodPair> getMethodMatching(final String regex) {
List<ClassMethodPair> classMethodPairs = new ArrayList<>();
// Get all of the test classes
for (Class<?> c : getAllContainedBaseTests()) {
for (Method m : c.getMethods()) {
// If this is a test method that is not trying to be ignored and it
// matches the regex
if (m.getAnnotation(Test.class) != null
&& m.getAnnotation(Ignore.class) == null
&& Pattern.matches(regex, m.getName())) {
ClassMethodPair pair = new ClassMethodPair(c, m);
classMethodPairs.add(pair);
}
}
}
return classMethodPairs;
}
/**
* 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.
*/
private List<Class<?>> getAllContainedBaseTests(List<Class<?>> runningList) {
for (Class<?> c : getAnnotatedTestClasses()) {
// Check to see if this is a test class or a suite
if (areAnySuperClassesOfTypeAbstractTestSuite(c)) {
// Create a new instance of this class so that we can retrieve its data
try {
AbstractTestSuite suite = (AbstractTestSuite) c.getDeclaredConstructor().newInstance();
// Add the tests from this suite that match the regex to the list of
// tests to run
runningList = suite.getAllContainedBaseTests(runningList);
} catch (NoSuchMethodException
| InvocationTargetException
| InstantiationException
| IllegalAccessException ex) {
// This shouldn't happen unless the constructor is changed in some
// way.
logger.log(
Level.SEVERE, "Test suites can not take parameters 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()));
} else { // This is a class containing tests
// so add it to the list
runningList.add(c);
}
}
return runningList;
}
/**
* 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() {
List<Class<?>> runningBaseTests = new ArrayList<>();
return getAllContainedBaseTests(runningBaseTests);
}
/**
* 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
*/
private List<Class<?>> getAllClassMatching(final String regex, final List<Class<?>> runningList) {
for (Class<?> c : getAnnotatedTestClasses()) {
// Compare the regex against the simple name of the class
if (Pattern.matches(regex, c.getName()) && !runningList.contains(c)) {
runningList.add(c);
}
}
return runningList;
}
/**
* 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
*/
public List<Class<?>> getAllClassMatching(final String regex) {
final List<Class<?>> matchingClasses = new ArrayList<>();
return getAllClassMatching(regex, matchingClasses);
}
/**
* 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) {
// Get any test suites matching the regex using the superclass methods
runningList = getAllClassMatching(regex, runningList);
// Then search any test suites not retrieved already for test classes
// matching the regex.
List<Class<?>> unCollectedSuites = getAllClasses();
// If we already have a test suite then we don't want to load the test twice
// so remove the suite from the list
unCollectedSuites.removeAll(runningList);
for (Class<?> c : unCollectedSuites) {
// Prevents recursively adding tests/suites that have already been added
if (!runningList.contains(c)) {
try {
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.
suite = (AbstractTestSuite) c.getDeclaredConstructor().newInstance();
// Add the tests from this suite that match the regex to the list of
// tests to run
runningList = suite.getSuiteOrTestMatchingRegex(regex, runningList);
}
} catch (NoSuchMethodException
| InvocationTargetException
| InstantiationException
| IllegalAccessException ex) {
// This shouldn't happen unless the constructor is changed in some
// way.
logger.log(
Level.SEVERE, "Test suites can not take parameters in their constructors.", ex);
}
}
}
return runningList;
}
/**
* 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.
*/
protected List<Class<?>> getSuiteOrTestMatchingRegex(final String regex) {
final List<Class<?>> matchingClasses = new ArrayList<>();
return getSuiteOrTestMatchingRegex(regex, matchingClasses);
}
/**
* 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.
*/
public List<Class<?>> getAllClasses() {
return getAnnotatedTestClasses();
}
/**
* 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.
*/
public List<String> getAllClassName() {
List<String> classNames = new ArrayList<>();
for (Class<?> c : getAnnotatedTestClasses()) {
classNames.add(c.getName());
}
return classNames;
}
}

View File

@@ -1,141 +0,0 @@
// 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.wpilibj.test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import edu.wpi.first.wpilibj.test.AbstractTestSuite.ClassMethodPair;
import java.util.List;
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;
/**
* Yes, this is the test system testing itself. Functionally, this is making sure that all tests get
* run correctly when you use parametrized arguments.
*/
@SuppressWarnings("MultipleTopLevelClasses")
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
})
static class TestForAbstractTestSuite extends AbstractTestSuite {}
TestForAbstractTestSuite m_testSuite;
@Before
public void setUp() {
m_testSuite = new TestForAbstractTestSuite();
}
@Test
public void testGetTestsMatchingAll() {
// when
List<Class<?>> collectedTests = m_testSuite.getAllClassMatching(".*");
// then
assertEquals(7, collectedTests.size());
}
@Test
public void testGetTestsMatchingSample() {
// when
List<Class<?>> collectedTests = m_testSuite.getAllClassMatching(".*Sample.*");
// then
assertEquals(4, collectedTests.size());
}
@Test
public void testGetTestsMatchingUnusual() {
// when
List<Class<?>> collectedTests = m_testSuite.getAllClassMatching(".*Unusual.*");
// then
assertEquals(1, collectedTests.size());
assertEquals(UnusualTest.class, collectedTests.get(0));
}
@Test
public void testGetTestsFromSuiteMatchingAll() {
// when
List<Class<?>> collectedTests = m_testSuite.getSuiteOrTestMatchingRegex(".*");
// then
assertEquals(7, collectedTests.size());
}
@Test
public void testGetTestsFromSuiteMatchingTest() {
// 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
public void testMethod() {}
}
@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

@@ -1,86 +0,0 @@
// 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.wpilibj.test;
import java.io.File;
import org.apache.tools.ant.BuildLogger;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
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;
/**
* Provides an entry point for tests to run with ANT. This allows ant to output JUnit XML test
* results for Jenkins.
*/
public class AntJunitLauncher {
/**
* Main entry point for jenkins.
*
* @param args Arguments passed to java.
*/
public static void main(String... args) {
if (args.length == 0) {
String pathToReports =
String.format("%s/%s", System.getProperty("user.dir"), "/testResults/AntReports");
Project project = new Project();
try {
// Create the file to store the test output
new File(pathToReports).mkdirs();
project.setProperty("java.io.tmpdir", pathToReports);
/* Output to screen */
FormatterElement.TypeAttribute typeScreen = new FormatterElement.TypeAttribute();
typeScreen.setValue("plain");
FormatterElement formatToScreen = new FormatterElement();
formatToScreen.setType(typeScreen);
formatToScreen.setUseFile(false);
formatToScreen.setOutput(System.out);
JUnitTask task = new JUnitTask();
task.addFormatter(formatToScreen);
// add a build listener to the project
BuildLogger logger = new DefaultLogger();
logger.setOutputPrintStream(System.out);
logger.setErrorPrintStream(System.err);
logger.setMessageOutputLevel(Project.MSG_INFO);
logger.setEmacsMode(true);
project.addBuildListener(logger);
task.setProject(project);
// Set the output to the XML file
FormatterElement.TypeAttribute type = new FormatterElement.TypeAttribute();
type.setValue("xml");
FormatterElement formatter = new FormatterElement();
formatter.setType(type);
task.addFormatter(formatter);
// Create the JUnitTest
JUnitTest test = new JUnitTest(TestSuite.class.getName());
test.setTodir(new File(pathToReports));
task.addTest(test);
TestBench.out().println("Beginning Test Execution With ANT");
task.execute();
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
TestBench.out()
.println(
"Run will not output XML for Jenkins because " + "tests are not being run with ANT");
// This should never return as it makes its own call to
// System.exit();
TestSuite.main(args);
}
System.exit(0);
}
}

View File

@@ -1,31 +0,0 @@
// 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.wpilibj.test;
import java.util.logging.Logger;
import org.junit.Test;
/**
* 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.
*/
public class QuickTest extends AbstractComsSetup {
private static final Logger logger = Logger.getLogger(QuickTest.class.getName());
/*
* (non-Javadoc)
*
* @see edu.wpi.first.wpilibj.test.AbstractComsSetup#getClassLogger()
*/
@Override
protected Logger getClassLogger() {
return logger;
}
@Test
public void test() {}
}

View File

@@ -1,58 +0,0 @@
// 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.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;
/**
* 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.
*
* <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>
*/
public class RepeatRule implements TestRule {
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.METHOD})
public @interface Repeat {
/** The number of times to repeat the test. */
int times();
}
private static class RepeatStatement extends Statement {
private final int m_times;
private final Statement m_statement;
private RepeatStatement(int times, Statement statement) {
m_times = times;
m_statement = statement;
}
@Override
public void evaluate() throws Throwable {
for (int i = 0; i < m_times; i++) {
m_statement.evaluate();
}
}
}
@Override
public Statement apply(Statement statement, Description description) {
Statement result = statement;
Repeat repeat = description.getAnnotation(Repeat.class);
if (repeat != null) {
int times = repeat.times();
result = new RepeatStatement(times, statement);
}
return result;
}
}

View File

@@ -1,334 +0,0 @@
// 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.wpilibj.test;
import edu.wpi.first.wpilibj.AnalogGyro;
import edu.wpi.first.wpilibj.AnalogInput;
import edu.wpi.first.wpilibj.AnalogOutput;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.Relay;
import edu.wpi.first.wpilibj.Servo;
import edu.wpi.first.wpilibj.fixtures.AnalogCrossConnectFixture;
import edu.wpi.first.wpilibj.fixtures.DIOCrossConnectFixture;
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.motorcontrol.Jaguar;
import edu.wpi.first.wpilibj.motorcontrol.Talon;
import edu.wpi.first.wpilibj.motorcontrol.Victor;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/** This class provides access to all of the elements on the test bench, for use in fixtures. */
public final class TestBench {
/**
* 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;
public static final int kTalonChannel = 10;
public static final int kVictorChannel = 1;
public static final int kJaguarChannel = 2;
/* TiltPanCamera Channels */
public static final int kGyroChannel = 0;
public static final double kGyroSensitivity = 0.013;
public static final int kTiltServoChannel = 9;
public static final int kPanServoChannel = 8;
/* PowerDistribution channels */
public static final int kJaguarPDPChannel = 6;
public static final int kVictorPDPChannel = 8;
public static final int kTalonPDPChannel = 10;
// THESE MUST BE IN INCREMENTING ORDER
public static final int DIOCrossConnectB2 = 9;
public static final int DIOCrossConnectB1 = 8;
public static final int DIOCrossConnectA2 = 7;
public static final int DIOCrossConnectA1 = 6;
/** The Singleton instance of the Test Bench. */
@SuppressWarnings("unused")
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.
*/
private TestBench() {}
/**
* Constructs a new set of objects representing a connected set of Talon controlled Motors and an
* encoder.
*
* @return a freshly allocated Talon, Encoder pair
*/
public static MotorEncoderFixture<Talon> getTalonPair() {
return new MotorEncoderFixture<>() {
@Override
protected Talon giveMotorController() {
return new Talon(kTalonChannel);
}
@Override
protected DigitalInput giveDigitalInputA() {
return new DigitalInput(0);
}
@Override
protected DigitalInput giveDigitalInputB() {
return new DigitalInput(1);
}
@Override
public int getPDPChannel() {
return kTalonPDPChannel;
}
};
}
/**
* Constructs a new set of objects representing a connected set of Victor controlled Motors and an
* encoder.
*
* @return a freshly allocated Victor, Encoder pair
*/
public static MotorEncoderFixture<Victor> getVictorPair() {
return new MotorEncoderFixture<>() {
@Override
protected Victor giveMotorController() {
return new Victor(kVictorChannel);
}
@Override
protected DigitalInput giveDigitalInputA() {
return new DigitalInput(2);
}
@Override
protected DigitalInput giveDigitalInputB() {
return new DigitalInput(3);
}
@Override
public int getPDPChannel() {
return kVictorPDPChannel;
}
};
}
/**
* Constructs a new set of objects representing a connected set of Jaguar controlled Motors and an
* encoder.
*
* @return a freshly allocated Jaguar, Encoder pair
*/
public static MotorEncoderFixture<Jaguar> getJaguarPair() {
return new MotorEncoderFixture<>() {
@Override
protected Jaguar giveMotorController() {
return new Jaguar(kJaguarChannel);
}
@Override
protected DigitalInput giveDigitalInputA() {
return new DigitalInput(4);
}
@Override
protected DigitalInput giveDigitalInputB() {
return new DigitalInput(5);
}
@Override
public int getPDPChannel() {
return kJaguarPDPChannel;
}
};
}
/**
* Constructs a new set of two Servo's and a Gyroscope.
*
* @return a freshly allocated Servo's and a freshly allocated Gyroscope
*/
public static TiltPanCameraFixture getTiltPanCam() {
return new TiltPanCameraFixture() {
@Override
protected AnalogGyro giveGyro() {
AnalogGyro gyro = new AnalogGyro(kGyroChannel);
gyro.setSensitivity(kGyroSensitivity);
return gyro;
}
@Override
protected AnalogGyro giveGyroParam(int center, double offset) {
AnalogGyro gyro = new AnalogGyro(kGyroChannel, center, offset);
gyro.setSensitivity(kGyroSensitivity);
return gyro;
}
@Override
protected Servo giveTilt() {
return new Servo(kTiltServoChannel);
}
@Override
protected Servo givePan() {
return new Servo(kPanServoChannel);
}
};
}
public static DIOCrossConnectFixture getDIOCrossConnectFixture(int inputPort, int outputPort) {
return new DIOCrossConnectFixture(inputPort, outputPort);
}
/** Gets two lists of possible DIO pairs for the two pairs. */
private static List<List<Integer[]>> getDIOCrossConnect() {
List<List<Integer[]>> pairs = new ArrayList<>();
List<Integer[]> setA =
List.of(
new Integer[][] {
{DIOCrossConnectA1, DIOCrossConnectA2},
{DIOCrossConnectA2, DIOCrossConnectA1}
});
pairs.add(setA);
List<Integer[]> setB =
List.of(
new Integer[][] {
{DIOCrossConnectB1, DIOCrossConnectB2},
{DIOCrossConnectB2, DIOCrossConnectB1}
});
pairs.add(setB);
// NOTE: IF MORE DIOCROSSCONNECT PAIRS ARE ADDED ADD THEM HERE
return pairs;
}
/** Returns the analog I/O cross-connect fixture. */
public static AnalogCrossConnectFixture getAnalogCrossConnectFixture() {
return new AnalogCrossConnectFixture() {
@Override
protected AnalogOutput giveAnalogOutput() {
return new AnalogOutput(0);
}
@Override
protected AnalogInput giveAnalogInput() {
return new AnalogInput(2);
}
};
}
/** Returns the relay cross-connect fixture. */
public static RelayCrossConnectFixture getRelayCrossConnectFixture() {
return new RelayCrossConnectFixture() {
@Override
protected Relay giveRelay() {
return new Relay(0);
}
@Override
protected DigitalInput giveInputTwo() {
return new DigitalInput(18);
}
@Override
protected DigitalInput giveInputOne() {
return new DigitalInput(19);
}
};
}
/**
* Return a single Collection containing all of the DIOCrossConnectFixtures in all two pair
* combinations.
*
* @return pairs of DIOCrossConnectFixtures
*/
public static Collection<Integer[]> getDIOCrossConnectCollection() {
Collection<Integer[]> pairs = new ArrayList<>();
for (Collection<Integer[]> collection : getDIOCrossConnect()) {
pairs.addAll(collection);
}
return pairs;
}
/**
* 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 static Collection<Integer[]> getPairArray(
List<Integer[]> listA, List<Integer[]> listB, boolean flip) {
Collection<Integer[]> encoderPortPairs = new ArrayList<>();
for (Integer[] portPairsA : listA) {
Integer[] inputs = new Integer[5];
inputs[0] = portPairsA[0]; // InputA
inputs[1] = portPairsA[1]; // InputB
for (Integer[] portPairsB : listB) {
inputs[2] = portPairsB[0]; // OutputA
inputs[3] = portPairsB[1]; // OutputB
inputs[4] = flip ? 0 : 1; // The flip bit
}
ArrayList<Integer[]> construtorInput = new ArrayList<>();
construtorInput.add(inputs);
inputs = inputs.clone();
for (Integer[] portPairsB : listB) {
inputs[2] = portPairsB[1]; // OutputA
inputs[3] = portPairsB[0]; // OutputB
inputs[4] = flip ? 0 : 1; // The flip bit
}
construtorInput.add(inputs);
encoderPortPairs.addAll(construtorInput);
}
return encoderPortPairs;
}
/**
* 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 static Collection<Integer[]> getEncoderDIOCrossConnectCollection() {
Collection<Integer[]> encoderPortPairs = new ArrayList<>();
assert getDIOCrossConnect().size() == 2;
encoderPortPairs.addAll(
getPairArray(getDIOCrossConnect().get(0), getDIOCrossConnect().get(1), false));
encoderPortPairs.addAll(
getPairArray(getDIOCrossConnect().get(1), getDIOCrossConnect().get(0), false));
assert encoderPortPairs.size() == 8;
return encoderPortPairs;
}
/**
* 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() {
return System.out;
}
/**
* 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() {
return System.err;
}
}

View File

@@ -1,391 +0,0 @@
// 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.wpilibj.test;
import edu.wpi.first.wpilibj.WpiLibJTestSuite;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import junit.framework.JUnit4TestAdapter;
import junit.runner.Version;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.RunWith;
import org.junit.runner.notification.Failure;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
/**
* 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)
@SuiteClasses(WpiLibJTestSuite.class)
public class TestSuite extends AbstractTestSuite {
static {
// Sets up the logging output
final InputStream inputStream = TestSuite.class.getResourceAsStream("/logging.properties");
try {
Objects.requireNonNull(inputStream, "./logging.properties was not loaded");
LogManager.getLogManager().readConfiguration(inputStream);
Logger.getAnonymousLogger().info("Loaded");
} catch (final IOException | NullPointerException ex) {
Logger.getAnonymousLogger().severe("Could not load default logging.properties file");
Logger.getAnonymousLogger().severe(ex.getMessage());
}
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
TestBench.out().println("Starting Tests");
}
@SuppressWarnings("unused")
private static final Logger WPILIBJ_ROOT_LOGGER = Logger.getLogger("edu.wpi.first.wpilibj");
@SuppressWarnings("unused")
private static final Logger WPILIBJ_COMMAND_ROOT_LOGGER =
Logger.getLogger("edu.wpi.first.wpilibj.command");
private static final Class<?> QUICK_TEST = QuickTest.class;
private static final String QUICK_TEST_FLAG = "--quick";
private static final String HELP_FLAG = "--help";
private static final String METHOD_NAME_FILTER = "--methodFilter=";
private static final String METHOD_REPEAT_FILTER = "--repeat=";
private static final String CLASS_NAME_FILTER = "--filter=";
private static TestSuite instance = null;
/** Get the singleton instance of the test suite. */
public static TestSuite getInstance() {
if (instance == null) {
instance = new TestSuite();
}
return instance;
}
/** This has to be public so that the JUnit4. */
public TestSuite() {}
/** Displays a help message for the user when they use the --help flag at runtime. */
protected static void displayHelp() {
String helpMessage =
"Test Parameters help: \n"
+ "\t"
+ QUICK_TEST_FLAG
+ " will cause the quick test to be run. Ignores other flags except for "
+ METHOD_REPEAT_FILTER
+ "\n"
+ "\t"
+ CLASS_NAME_FILTER
+ " will use the supplied regex text to search for suite/test class names "
+ "matching the regex and run them.\n"
+ "\t"
+ METHOD_NAME_FILTER
+ " will use the supplied regex text to search for test methods (excluding methods "
+ "with the @Ignore annotation) and run only those methods. Can be paired with "
+ METHOD_REPEAT_FILTER
+ " to "
+ "repeat the selected tests multiple times.\n"
+ "\t"
+ METHOD_REPEAT_FILTER
+ " will repeat the tests selected with either "
+ QUICK_TEST_FLAG
+ " or "
+ CLASS_NAME_FILTER
+ " and run them the given number of times.\n"
+ "[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"
+ "\n"
+ "\n";
TestBench.out().println(helpMessage);
}
/**
* 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");
invalidMessage.append("Params received: ");
for (String a : args) {
invalidMessage.append(a).append(" ");
}
invalidMessage.append("\n");
invalidMessage.append(
"For details on proper usage of the runtime flags please run again with the "
+ HELP_FLAG
+ " flag.\n\n");
TestBench.out().println(invalidMessage);
}
/**
* 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 packagE = null;
for (Class<?> c : classes) {
if (c.getPackage().equals(packagE)) {
packagE = c.getPackage();
loadedTestsMessage.append(packagE.getName()).append("\n");
}
loadedTestsMessage.append("\t").append(c.getSimpleName()).append("\n");
}
TestBench.out().println(loadedTestsMessage);
}
/**
* 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.
*/
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.
printLoadedTests(TestSuite.class);
return JUnitCore.runClasses(TestSuite.class);
}
// The method filter was passed
boolean methodFilter = false;
String methodRegex = "";
// The class filter was passed
boolean classFilter = false;
String classRegex = "";
int repeatCount = 1;
for (String s : args) {
if (Pattern.matches(METHOD_NAME_FILTER + ".*", s)) {
methodFilter = true;
methodRegex = s.replace(METHOD_NAME_FILTER, "");
}
if (Pattern.matches(METHOD_REPEAT_FILTER + ".*", s)) {
try {
repeatCount = Integer.parseInt(s.replace(METHOD_REPEAT_FILTER, ""));
} 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 = s.replace(CLASS_NAME_FILTER, "");
}
}
ArrayList<String> argsParsed = new ArrayList<>(List.of(args));
if (argsParsed.contains(HELP_FLAG)) {
// If the user inputs the help flag then return the help message and exit
// without running any tests
displayHelp();
return null;
}
if (argsParsed.contains(QUICK_TEST_FLAG)) {
printLoadedTests(QUICK_TEST);
return JUnitCore.runClasses(QUICK_TEST);
}
/**
* Stores the data from multiple {@link Result}s in one class that can be returned to display
* the results.
*/
class MultipleResult extends Result {
private static final long serialVersionUID = 1L;
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 m_runCount;
}
@Override
public int getFailureCount() {
return m_failures.size();
}
@Override
public long getRunTime() {
return m_runTime;
}
@Override
public List<Failure> getFailures() {
return m_failures;
}
@Override
public int getIgnoreCount() {
return m_ignoreCount;
}
/**
* Adds the given result's data to this result.
*
* @param result the result to add to this result
*/
void addResult(Result result) {
m_failures.addAll(result.getFailures());
m_runCount += result.getRunCount();
m_ignoreCount += result.getIgnoreCount();
m_runTime += result.getRunTime();
}
}
// If a specific method has been requested
if (methodFilter) {
List<ClassMethodPair> pairs = (new TestSuite()).getMethodMatching(methodRegex);
if (pairs.isEmpty()) {
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.m_methodClass.equals(classListing)) {
// Only display the class name every time it changes
classListing = p.m_methodClass;
TestBench.out().println(classListing);
}
TestBench.out().println("\t" + p.m_methodName);
}
// The test results will be saved here
MultipleResult results = new MultipleResult();
// Runs tests multiple times if the repeat rule is used
for (int i = 0; i < repeatCount; i++) {
// Now run all of the tests
for (ClassMethodPair p : pairs) {
Result result = (new JUnitCore()).run(p.getMethodRunRequest());
// Store the given results in one cohesive result
results.addResult(result);
}
}
return results;
}
// If a specific class has been requested
if (classFilter) {
List<Class<?>> testClasses = (new TestSuite()).getSuiteOrTestMatchingRegex(classRegex);
if (testClasses.isEmpty()) {
displayInvalidUsage("None of the arguments passed to the filter matched.", args);
return null;
}
printLoadedTests(testClasses.toArray(new Class[0]));
MultipleResult results = new MultipleResult();
// Runs tests multiple times if the repeat rule is used
for (int i = 0; i < repeatCount; i++) {
Result result = (new JUnitCore()).run(testClasses.toArray(new Class[0]));
// Store the given results in one cohesive result
results.addResult(result);
}
return results;
}
displayInvalidUsage(
"None of the parameters that you passed matched any of the accepted flags.", args);
return null;
}
protected static void displayResults(Result result) {
// Results are collected and displayed here
TestBench.out().println("\n");
if (!result.wasSuccessful()) {
// Prints out a list of stack traces for the failed tests
TestBench.out().println("Failure List: ");
for (Failure f : result.getFailures()) {
TestBench.out().println(f.getDescription());
TestBench.out().println(f.getTrace());
}
TestBench.out().println();
TestBench.out().println("FAILURES!!!");
// Print the test statistics
TestBench.out()
.println(
"Tests run: "
+ result.getRunCount()
+ ", Failures: "
+ result.getFailureCount()
+ ", Ignored: "
+ result.getIgnoreCount()
+ ", In "
+ result.getRunTime()
+ "ms");
// Prints out a list of test that failed
TestBench.out().println("Failure List (short):");
String failureClass = result.getFailures().get(0).getDescription().getClassName();
TestBench.out().println(failureClass);
for (Failure f : result.getFailures()) {
if (!failureClass.equals(f.getDescription().getClassName())) {
failureClass = f.getDescription().getClassName();
TestBench.out().println(failureClass);
}
TestBench.err().println("\t" + f.getDescription());
}
} else {
TestBench.out().println("SUCCESS!");
TestBench.out()
.println(
"Tests run: "
+ result.getRunCount()
+ ", Ignored: "
+ result.getIgnoreCount()
+ ", In "
+ result.getRunTime()
+ "ms");
}
TestBench.out().println();
}
/**
* 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);
}
/**
* The method called at runtime.
*
* @param args The test suites to run
*/
public static void main(String[] args) {
TestBench.out().println("JUnit version " + Version.id());
// Tests are run here
Result result = parseArgsRunAndGetResult(args);
if (result != null) {
// Results are collected and displayed here
displayResults(result);
System.exit(result.wasSuccessful() ? 0 : 1);
}
System.exit(1);
}
}

View File

@@ -1,14 +0,0 @@
// 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.
/**
* 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;

View File

@@ -1,25 +0,0 @@
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers=java.util.logging.ConsoleHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overridden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
#.level= INFO
.level=INFO
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
java.util.logging.ConsoleHandler.level=FINER
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
edu.wpi.first.wpilibj.level=INFO
edu.wpi.first.wpilibj.command.level=INFO