mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
Remove CrossConn and Integration Tests (#7692)
This commit is contained in:
@@ -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"
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
@@ -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));
|
||||
@@ -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));
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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));
|
||||
@@ -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));
|
||||
@@ -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));
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
};
|
||||
@@ -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() {}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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'
|
||||
|
||||
4
test-scripts/.gitattributes
vendored
4
test-scripts/.gitattributes
vendored
@@ -1,4 +0,0 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
*.sh text eol=lf
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}
|
||||
@@ -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
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.";
|
||||
}
|
||||
@@ -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.";
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.";
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
};
|
||||
@@ -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() {}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user