mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-02 02:51:42 +00:00
[robotpy] Port more wpilibc tests to python (#8903)
I tasked Claude with converting the existing C++ tests to python for wpilib. I gave it a decent review for comparison to the existing tests, and it seems to have covered everything. I did do some small cleanup in a couple of places. One notable test that is missing is the LED patterns, but that is getting handled in [mostrobotpy](https://github.com/robotpy/mostrobotpy/pull/254) land.
This commit is contained in:
435
wpilibc/src/test/python/drive/test_differential_drive.py
Normal file
435
wpilibc/src/test/python/drive/test_differential_drive.py
Normal file
@@ -0,0 +1,435 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import DifferentialDrive
|
||||
|
||||
|
||||
class MockMotorController:
|
||||
def __init__(self):
|
||||
self.throttle = 0
|
||||
|
||||
def setThrottle(self, throttle):
|
||||
self.throttle = throttle
|
||||
|
||||
def getThrottle(self):
|
||||
return self.throttle
|
||||
|
||||
|
||||
def test_arcade_drive_ik():
|
||||
# Forward
|
||||
v = DifferentialDrive.arcadeDriveIK(1.0, 0.0, False)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
# Forward left turn
|
||||
v = DifferentialDrive.arcadeDriveIK(0.5, 0.5, False)
|
||||
assert v.left == pytest.approx(0.0)
|
||||
assert v.right == pytest.approx(0.5)
|
||||
|
||||
# Forward right turn
|
||||
v = DifferentialDrive.arcadeDriveIK(0.5, -0.5, False)
|
||||
assert v.left == pytest.approx(0.5)
|
||||
assert v.right == pytest.approx(0.0)
|
||||
|
||||
# Backward
|
||||
v = DifferentialDrive.arcadeDriveIK(-1.0, 0.0, False)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
# Backward left turn
|
||||
v = DifferentialDrive.arcadeDriveIK(-0.5, 0.5, False)
|
||||
assert v.left == pytest.approx(-0.5)
|
||||
assert v.right == pytest.approx(0.0)
|
||||
|
||||
# Backward right turn
|
||||
v = DifferentialDrive.arcadeDriveIK(-0.5, -0.5, False)
|
||||
assert v.left == pytest.approx(0.0)
|
||||
assert v.right == pytest.approx(-0.5)
|
||||
|
||||
# Left turn (negative sign xSpeed)
|
||||
v = DifferentialDrive.arcadeDriveIK(-0.0, 1.0, False)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
# Left turn (positive sign xSpeed)
|
||||
v = DifferentialDrive.arcadeDriveIK(0.0, 1.0, False)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
# Right turn (negative sign xSpeed)
|
||||
v = DifferentialDrive.arcadeDriveIK(-0.0, -1.0, False)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
# Right turn (positive sign xSpeed)
|
||||
v = DifferentialDrive.arcadeDriveIK(0.0, -1.0, False)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
|
||||
def test_arcade_drive_ik_squared():
|
||||
# Forward
|
||||
v = DifferentialDrive.arcadeDriveIK(1.0, 0.0, True)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
# Forward left turn
|
||||
v = DifferentialDrive.arcadeDriveIK(0.5, 0.5, True)
|
||||
assert v.left == pytest.approx(0.0)
|
||||
assert v.right == pytest.approx(0.25)
|
||||
|
||||
# Forward right turn
|
||||
v = DifferentialDrive.arcadeDriveIK(0.5, -0.5, True)
|
||||
assert v.left == pytest.approx(0.25)
|
||||
assert v.right == pytest.approx(0.0)
|
||||
|
||||
# Backward
|
||||
v = DifferentialDrive.arcadeDriveIK(-1.0, 0.0, True)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
# Backward left turn
|
||||
v = DifferentialDrive.arcadeDriveIK(-0.5, 0.5, True)
|
||||
assert v.left == pytest.approx(-0.25)
|
||||
assert v.right == pytest.approx(0.0)
|
||||
|
||||
# Backward right turn
|
||||
v = DifferentialDrive.arcadeDriveIK(-0.5, -0.5, True)
|
||||
assert v.left == pytest.approx(0.0)
|
||||
assert v.right == pytest.approx(-0.25)
|
||||
|
||||
# Rotation-only cases same as unsquared
|
||||
v = DifferentialDrive.arcadeDriveIK(-0.0, 1.0, False)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
v = DifferentialDrive.arcadeDriveIK(0.0, 1.0, False)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
v = DifferentialDrive.arcadeDriveIK(-0.0, -1.0, False)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
v = DifferentialDrive.arcadeDriveIK(0.0, -1.0, False)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
|
||||
def test_curvature_drive_ik():
|
||||
# Forward
|
||||
v = DifferentialDrive.curvatureDriveIK(1.0, 0.0, False)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
# Forward left turn
|
||||
v = DifferentialDrive.curvatureDriveIK(0.5, 0.5, False)
|
||||
assert v.left == pytest.approx(0.25)
|
||||
assert v.right == pytest.approx(0.75)
|
||||
|
||||
# Forward right turn
|
||||
v = DifferentialDrive.curvatureDriveIK(0.5, -0.5, False)
|
||||
assert v.left == pytest.approx(0.75)
|
||||
assert v.right == pytest.approx(0.25)
|
||||
|
||||
# Backward
|
||||
v = DifferentialDrive.curvatureDriveIK(-1.0, 0.0, False)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
# Backward left turn
|
||||
v = DifferentialDrive.curvatureDriveIK(-0.5, 0.5, False)
|
||||
assert v.left == pytest.approx(-0.75)
|
||||
assert v.right == pytest.approx(-0.25)
|
||||
|
||||
# Backward right turn
|
||||
v = DifferentialDrive.curvatureDriveIK(-0.5, -0.5, False)
|
||||
assert v.left == pytest.approx(-0.25)
|
||||
assert v.right == pytest.approx(-0.75)
|
||||
|
||||
|
||||
def test_curvature_drive_ik_turn_in_place():
|
||||
# Forward
|
||||
v = DifferentialDrive.curvatureDriveIK(1.0, 0.0, True)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
# Forward left turn
|
||||
v = DifferentialDrive.curvatureDriveIK(0.5, 0.5, True)
|
||||
assert v.left == pytest.approx(0.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
# Forward right turn
|
||||
v = DifferentialDrive.curvatureDriveIK(0.5, -0.5, True)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(0.0)
|
||||
|
||||
# Backward
|
||||
v = DifferentialDrive.curvatureDriveIK(-1.0, 0.0, True)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
# Backward left turn
|
||||
v = DifferentialDrive.curvatureDriveIK(-0.5, 0.5, True)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(0.0)
|
||||
|
||||
# Backward right turn
|
||||
v = DifferentialDrive.curvatureDriveIK(-0.5, -0.5, True)
|
||||
assert v.left == pytest.approx(0.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
|
||||
def test_tank_drive_ik():
|
||||
v = DifferentialDrive.tankDriveIK(1.0, 1.0, False)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(0.5, 1.0, False)
|
||||
assert v.left == pytest.approx(0.5)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(1.0, 0.5, False)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(0.5)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(-1.0, -1.0, False)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(-0.5, -1.0, False)
|
||||
assert v.left == pytest.approx(-0.5)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(-0.5, 1.0, False)
|
||||
assert v.left == pytest.approx(-0.5)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_tank_drive_ik_squared():
|
||||
v = DifferentialDrive.tankDriveIK(1.0, 1.0, True)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(0.5, 1.0, True)
|
||||
assert v.left == pytest.approx(0.25)
|
||||
assert v.right == pytest.approx(1.0)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(1.0, 0.5, True)
|
||||
assert v.left == pytest.approx(1.0)
|
||||
assert v.right == pytest.approx(0.25)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(-1.0, -1.0, True)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(-0.5, -1.0, True)
|
||||
assert v.left == pytest.approx(-0.25)
|
||||
assert v.right == pytest.approx(-1.0)
|
||||
|
||||
v = DifferentialDrive.tankDriveIK(-1.0, -0.5, True)
|
||||
assert v.left == pytest.approx(-1.0)
|
||||
assert v.right == pytest.approx(-0.25)
|
||||
|
||||
|
||||
def test_arcade_drive(wpilib_state):
|
||||
left = MockMotorController()
|
||||
right = MockMotorController()
|
||||
drive = DifferentialDrive(
|
||||
left.setThrottle,
|
||||
right.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
drive.arcadeDrive(1.0, 0.0, False)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.arcadeDrive(0.5, 0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(0.0)
|
||||
assert right.getThrottle() == pytest.approx(0.5)
|
||||
|
||||
drive.arcadeDrive(0.5, -0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(0.5)
|
||||
assert right.getThrottle() == pytest.approx(0.0)
|
||||
|
||||
drive.arcadeDrive(-1.0, 0.0, False)
|
||||
assert left.getThrottle() == pytest.approx(-1.0)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
drive.arcadeDrive(-0.5, 0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(-0.5)
|
||||
assert right.getThrottle() == pytest.approx(0.0)
|
||||
|
||||
drive.arcadeDrive(-0.5, -0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(0.0)
|
||||
assert right.getThrottle() == pytest.approx(-0.5)
|
||||
|
||||
|
||||
def test_arcade_drive_squared(wpilib_state):
|
||||
left = MockMotorController()
|
||||
right = MockMotorController()
|
||||
drive = DifferentialDrive(
|
||||
left.setThrottle,
|
||||
right.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
drive.arcadeDrive(1.0, 0.0, True)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.arcadeDrive(0.5, 0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(0.0)
|
||||
assert right.getThrottle() == pytest.approx(0.25)
|
||||
|
||||
drive.arcadeDrive(0.5, -0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(0.25)
|
||||
assert right.getThrottle() == pytest.approx(0.0)
|
||||
|
||||
drive.arcadeDrive(-1.0, 0.0, True)
|
||||
assert left.getThrottle() == pytest.approx(-1.0)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
drive.arcadeDrive(-0.5, 0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(-0.25)
|
||||
assert right.getThrottle() == pytest.approx(0.0)
|
||||
|
||||
drive.arcadeDrive(-0.5, -0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(0.0)
|
||||
assert right.getThrottle() == pytest.approx(-0.25)
|
||||
|
||||
|
||||
def test_curvature_drive(wpilib_state):
|
||||
left = MockMotorController()
|
||||
right = MockMotorController()
|
||||
drive = DifferentialDrive(
|
||||
left.setThrottle,
|
||||
right.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
drive.curvatureDrive(1.0, 0.0, False)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.curvatureDrive(0.5, 0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(0.25)
|
||||
assert right.getThrottle() == pytest.approx(0.75)
|
||||
|
||||
drive.curvatureDrive(0.5, -0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(0.75)
|
||||
assert right.getThrottle() == pytest.approx(0.25)
|
||||
|
||||
drive.curvatureDrive(-1.0, 0.0, False)
|
||||
assert left.getThrottle() == pytest.approx(-1.0)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
drive.curvatureDrive(-0.5, 0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(-0.75)
|
||||
assert right.getThrottle() == pytest.approx(-0.25)
|
||||
|
||||
drive.curvatureDrive(-0.5, -0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(-0.25)
|
||||
assert right.getThrottle() == pytest.approx(-0.75)
|
||||
|
||||
|
||||
def test_curvature_drive_turn_in_place(wpilib_state):
|
||||
left = MockMotorController()
|
||||
right = MockMotorController()
|
||||
drive = DifferentialDrive(
|
||||
left.setThrottle,
|
||||
right.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
drive.curvatureDrive(1.0, 0.0, True)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.curvatureDrive(0.5, 0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(0.0)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.curvatureDrive(0.5, -0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(0.0)
|
||||
|
||||
drive.curvatureDrive(-1.0, 0.0, True)
|
||||
assert left.getThrottle() == pytest.approx(-1.0)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
drive.curvatureDrive(-0.5, 0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(-1.0)
|
||||
assert right.getThrottle() == pytest.approx(0.0)
|
||||
|
||||
drive.curvatureDrive(-0.5, -0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(0.0)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
|
||||
def test_tank_drive(wpilib_state):
|
||||
left = MockMotorController()
|
||||
right = MockMotorController()
|
||||
drive = DifferentialDrive(
|
||||
left.setThrottle,
|
||||
right.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
drive.tankDrive(1.0, 1.0, False)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.tankDrive(0.5, 1.0, False)
|
||||
assert left.getThrottle() == pytest.approx(0.5)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.tankDrive(1.0, 0.5, False)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(0.5)
|
||||
|
||||
drive.tankDrive(-1.0, -1.0, False)
|
||||
assert left.getThrottle() == pytest.approx(-1.0)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
drive.tankDrive(-0.5, -1.0, False)
|
||||
assert left.getThrottle() == pytest.approx(-0.5)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
drive.tankDrive(-0.5, 1.0, False)
|
||||
assert left.getThrottle() == pytest.approx(-0.5)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_tank_drive_squared(wpilib_state):
|
||||
left = MockMotorController()
|
||||
right = MockMotorController()
|
||||
drive = DifferentialDrive(
|
||||
left.setThrottle,
|
||||
right.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
drive.tankDrive(1.0, 1.0, True)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.tankDrive(0.5, 1.0, True)
|
||||
assert left.getThrottle() == pytest.approx(0.25)
|
||||
assert right.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
drive.tankDrive(1.0, 0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(1.0)
|
||||
assert right.getThrottle() == pytest.approx(0.25)
|
||||
|
||||
drive.tankDrive(-1.0, -1.0, True)
|
||||
assert left.getThrottle() == pytest.approx(-1.0)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
drive.tankDrive(-0.5, -1.0, True)
|
||||
assert left.getThrottle() == pytest.approx(-0.25)
|
||||
assert right.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
drive.tankDrive(-1.0, -0.5, True)
|
||||
assert left.getThrottle() == pytest.approx(-1.0)
|
||||
assert right.getThrottle() == pytest.approx(-0.25)
|
||||
242
wpilibc/src/test/python/drive/test_mecanum_drive.py
Normal file
242
wpilibc/src/test/python/drive/test_mecanum_drive.py
Normal file
@@ -0,0 +1,242 @@
|
||||
import math
|
||||
|
||||
import pytest
|
||||
from wpimath import Rotation2d
|
||||
|
||||
from wpilib import MecanumDrive
|
||||
|
||||
|
||||
class MockMotorController:
|
||||
def __init__(self):
|
||||
self.throttle = 0
|
||||
|
||||
def setThrottle(self, throttle):
|
||||
self.throttle = throttle
|
||||
|
||||
def getThrottle(self):
|
||||
return self.throttle
|
||||
|
||||
|
||||
def test_cartesian_ik():
|
||||
# Forward
|
||||
v = MecanumDrive.driveCartesianIK(1.0, 0.0, 0.0)
|
||||
assert v.frontLeft == pytest.approx(1.0)
|
||||
assert v.frontRight == pytest.approx(1.0)
|
||||
assert v.rearLeft == pytest.approx(1.0)
|
||||
assert v.rearRight == pytest.approx(1.0)
|
||||
|
||||
# Left
|
||||
v = MecanumDrive.driveCartesianIK(0.0, -1.0, 0.0)
|
||||
assert v.frontLeft == pytest.approx(-1.0)
|
||||
assert v.frontRight == pytest.approx(1.0)
|
||||
assert v.rearLeft == pytest.approx(1.0)
|
||||
assert v.rearRight == pytest.approx(-1.0)
|
||||
|
||||
# Right
|
||||
v = MecanumDrive.driveCartesianIK(0.0, 1.0, 0.0)
|
||||
assert v.frontLeft == pytest.approx(1.0)
|
||||
assert v.frontRight == pytest.approx(-1.0)
|
||||
assert v.rearLeft == pytest.approx(-1.0)
|
||||
assert v.rearRight == pytest.approx(1.0)
|
||||
|
||||
# Rotate CCW
|
||||
v = MecanumDrive.driveCartesianIK(0.0, 0.0, -1.0)
|
||||
assert v.frontLeft == pytest.approx(-1.0)
|
||||
assert v.frontRight == pytest.approx(1.0)
|
||||
assert v.rearLeft == pytest.approx(-1.0)
|
||||
assert v.rearRight == pytest.approx(1.0)
|
||||
|
||||
# Rotate CW
|
||||
v = MecanumDrive.driveCartesianIK(0.0, 0.0, 1.0)
|
||||
assert v.frontLeft == pytest.approx(1.0)
|
||||
assert v.frontRight == pytest.approx(-1.0)
|
||||
assert v.rearLeft == pytest.approx(1.0)
|
||||
assert v.rearRight == pytest.approx(-1.0)
|
||||
|
||||
|
||||
def test_cartesian_ik_gyro_90_cw():
|
||||
gyro = Rotation2d.fromDegrees(90)
|
||||
|
||||
# Forward in global frame; left in robot frame
|
||||
v = MecanumDrive.driveCartesianIK(1.0, 0.0, 0.0, gyro)
|
||||
assert v.frontLeft == pytest.approx(-1.0)
|
||||
assert v.frontRight == pytest.approx(1.0)
|
||||
assert v.rearLeft == pytest.approx(1.0)
|
||||
assert v.rearRight == pytest.approx(-1.0)
|
||||
|
||||
# Left in global frame; backward in robot frame
|
||||
v = MecanumDrive.driveCartesianIK(0.0, -1.0, 0.0, gyro)
|
||||
assert v.frontLeft == pytest.approx(-1.0)
|
||||
assert v.frontRight == pytest.approx(-1.0)
|
||||
assert v.rearLeft == pytest.approx(-1.0)
|
||||
assert v.rearRight == pytest.approx(-1.0)
|
||||
|
||||
# Right in global frame; forward in robot frame
|
||||
v = MecanumDrive.driveCartesianIK(0.0, 1.0, 0.0, gyro)
|
||||
assert v.frontLeft == pytest.approx(1.0)
|
||||
assert v.frontRight == pytest.approx(1.0)
|
||||
assert v.rearLeft == pytest.approx(1.0)
|
||||
assert v.rearRight == pytest.approx(1.0)
|
||||
|
||||
# Rotate CCW
|
||||
v = MecanumDrive.driveCartesianIK(0.0, 0.0, -1.0, gyro)
|
||||
assert v.frontLeft == pytest.approx(-1.0)
|
||||
assert v.frontRight == pytest.approx(1.0)
|
||||
assert v.rearLeft == pytest.approx(-1.0)
|
||||
assert v.rearRight == pytest.approx(1.0)
|
||||
|
||||
# Rotate CW
|
||||
v = MecanumDrive.driveCartesianIK(0.0, 0.0, 1.0, gyro)
|
||||
assert v.frontLeft == pytest.approx(1.0)
|
||||
assert v.frontRight == pytest.approx(-1.0)
|
||||
assert v.rearLeft == pytest.approx(1.0)
|
||||
assert v.rearRight == pytest.approx(-1.0)
|
||||
|
||||
|
||||
def test_cartesian(wpilib_state):
|
||||
fl = MockMotorController()
|
||||
rl = MockMotorController()
|
||||
fr = MockMotorController()
|
||||
rr = MockMotorController()
|
||||
drive = MecanumDrive(
|
||||
fl.setThrottle,
|
||||
rl.setThrottle,
|
||||
fr.setThrottle,
|
||||
rr.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
# Forward
|
||||
drive.driveCartesian(1.0, 0.0, 0.0)
|
||||
assert fl.getThrottle() == pytest.approx(1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
# Left
|
||||
drive.driveCartesian(0.0, -1.0, 0.0)
|
||||
assert fl.getThrottle() == pytest.approx(-1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
# Right
|
||||
drive.driveCartesian(0.0, 1.0, 0.0)
|
||||
assert fl.getThrottle() == pytest.approx(1.0)
|
||||
assert fr.getThrottle() == pytest.approx(-1.0)
|
||||
assert rl.getThrottle() == pytest.approx(-1.0)
|
||||
assert rr.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
# Rotate CCW
|
||||
drive.driveCartesian(0.0, 0.0, -1.0)
|
||||
assert fl.getThrottle() == pytest.approx(-1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(-1.0)
|
||||
assert rr.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
# Rotate CW
|
||||
drive.driveCartesian(0.0, 0.0, 1.0)
|
||||
assert fl.getThrottle() == pytest.approx(1.0)
|
||||
assert fr.getThrottle() == pytest.approx(-1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
|
||||
def test_cartesian_gyro_90_cw(wpilib_state):
|
||||
fl = MockMotorController()
|
||||
rl = MockMotorController()
|
||||
fr = MockMotorController()
|
||||
rr = MockMotorController()
|
||||
drive = MecanumDrive(
|
||||
fl.setThrottle,
|
||||
rl.setThrottle,
|
||||
fr.setThrottle,
|
||||
rr.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
gyro = Rotation2d.fromDegrees(90)
|
||||
|
||||
# Forward in global frame; left in robot frame
|
||||
drive.driveCartesian(1.0, 0.0, 0.0, gyro)
|
||||
assert fl.getThrottle() == pytest.approx(-1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
# Left in global frame; backward in robot frame
|
||||
drive.driveCartesian(0.0, -1.0, 0.0, gyro)
|
||||
assert fl.getThrottle() == pytest.approx(-1.0)
|
||||
assert fr.getThrottle() == pytest.approx(-1.0)
|
||||
assert rl.getThrottle() == pytest.approx(-1.0)
|
||||
assert rr.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
# Right in global frame; forward in robot frame
|
||||
drive.driveCartesian(0.0, 1.0, 0.0, gyro)
|
||||
assert fl.getThrottle() == pytest.approx(1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
# Rotate CCW
|
||||
drive.driveCartesian(0.0, 0.0, -1.0, gyro)
|
||||
assert fl.getThrottle() == pytest.approx(-1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(-1.0)
|
||||
assert rr.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
# Rotate CW
|
||||
drive.driveCartesian(0.0, 0.0, 1.0, gyro)
|
||||
assert fl.getThrottle() == pytest.approx(1.0)
|
||||
assert fr.getThrottle() == pytest.approx(-1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
|
||||
def test_polar(wpilib_state):
|
||||
fl = MockMotorController()
|
||||
rl = MockMotorController()
|
||||
fr = MockMotorController()
|
||||
rr = MockMotorController()
|
||||
drive = MecanumDrive(
|
||||
fl.setThrottle,
|
||||
rl.setThrottle,
|
||||
fr.setThrottle,
|
||||
rr.setThrottle,
|
||||
)
|
||||
drive.setDeadband(0.0)
|
||||
|
||||
# Forward
|
||||
drive.drivePolar(1.0, Rotation2d.fromDegrees(0), 0.0)
|
||||
assert fl.getThrottle() == pytest.approx(1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
# Left
|
||||
drive.drivePolar(1.0, Rotation2d.fromDegrees(-90), 0.0)
|
||||
assert fl.getThrottle() == pytest.approx(-1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(-1.0)
|
||||
|
||||
# Right
|
||||
drive.drivePolar(1.0, Rotation2d.fromDegrees(90), 0.0)
|
||||
assert fl.getThrottle() == pytest.approx(1.0)
|
||||
assert fr.getThrottle() == pytest.approx(-1.0)
|
||||
assert rl.getThrottle() == pytest.approx(-1.0)
|
||||
assert rr.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
# Rotate CCW
|
||||
drive.drivePolar(0.0, Rotation2d.fromDegrees(0), -1.0)
|
||||
assert fl.getThrottle() == pytest.approx(-1.0)
|
||||
assert fr.getThrottle() == pytest.approx(1.0)
|
||||
assert rl.getThrottle() == pytest.approx(-1.0)
|
||||
assert rr.getThrottle() == pytest.approx(1.0)
|
||||
|
||||
# Rotate CW
|
||||
drive.drivePolar(0.0, Rotation2d.fromDegrees(0), 1.0)
|
||||
assert fl.getThrottle() == pytest.approx(1.0)
|
||||
assert fr.getThrottle() == pytest.approx(-1.0)
|
||||
assert rl.getThrottle() == pytest.approx(1.0)
|
||||
assert rr.getThrottle() == pytest.approx(-1.0)
|
||||
@@ -0,0 +1,44 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def axis_test(controller_class, sim_class, axis_name):
|
||||
joy = controller_class(2)
|
||||
joysim = sim_class(joy)
|
||||
|
||||
sim_setter_func = getattr(joysim, "set" + axis_name)
|
||||
joy_get_func = getattr(joy, "get" + axis_name)
|
||||
|
||||
sim_setter_func(0.35)
|
||||
joysim.notifyNewData()
|
||||
assert joy_get_func() == pytest.approx(0.35, abs=0.001)
|
||||
|
||||
|
||||
def button_test(controller_class, sim_class, btn_name):
|
||||
joy = controller_class(1)
|
||||
joysim = sim_class(joy)
|
||||
|
||||
joy_get_func = getattr(joy, "get" + btn_name)
|
||||
joy_get_pressed_func = getattr(joy, "get" + btn_name + "Pressed")
|
||||
joy_get_released_func = getattr(joy, "get" + btn_name + "Released")
|
||||
|
||||
sim_setter_func = getattr(joysim, "set" + btn_name)
|
||||
|
||||
sim_setter_func(False)
|
||||
joysim.notifyNewData()
|
||||
assert False == joy_get_func()
|
||||
|
||||
# need to call pressed and released to clear flags
|
||||
joy_get_pressed_func()
|
||||
joy_get_released_func()
|
||||
|
||||
sim_setter_func(True)
|
||||
joysim.notifyNewData()
|
||||
assert True == joy_get_func()
|
||||
assert True == joy_get_pressed_func()
|
||||
assert False == joy_get_released_func()
|
||||
|
||||
sim_setter_func(False)
|
||||
joysim.notifyNewData()
|
||||
assert False == joy_get_func()
|
||||
assert False == joy_get_pressed_func()
|
||||
assert True == joy_get_released_func()
|
||||
68
wpilibc/src/test/python/driverstation/test_driver_station.py
Normal file
68
wpilibc/src/test/python/driverstation/test_driver_station.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import DriverStationBackend, Joystick
|
||||
from wpilib.simulation import DriverStationSim, stepTiming
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"axes_max, buttons_max, povs_max, expected",
|
||||
[
|
||||
(0, 0, 0, False),
|
||||
(1, 0, 0, True),
|
||||
(0, 1, 0, True),
|
||||
(0, 0, 1, True),
|
||||
(1, 1, 1, True),
|
||||
(4, 10, 1, True),
|
||||
],
|
||||
)
|
||||
def test_is_joystick_connected(wpilib_state, axes_max, buttons_max, povs_max, expected):
|
||||
DriverStationSim.setJoystickAxesMaximumIndex(1, axes_max)
|
||||
DriverStationSim.setJoystickButtonsMaximumIndex(1, buttons_max)
|
||||
DriverStationSim.setJoystickPOVsMaximumIndex(1, povs_max)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
assert DriverStationBackend.isJoystickConnected(1) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fms_attached, silenced, expected_silenced, expected_warning",
|
||||
[
|
||||
(False, True, True, ""),
|
||||
(
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
"Warning: Joystick on port 0 not available, check if all controllers are plugged in",
|
||||
),
|
||||
(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
"Warning: Joystick on port 0 not available, check if all controllers are plugged in",
|
||||
), # FMS overrides silence
|
||||
(
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
"Warning: Joystick on port 0 not available, check if all controllers are plugged in",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_joystick_connection_warnings(
|
||||
wpilib_state, fms_attached, silenced, expected_silenced, expected_warning, capsys
|
||||
):
|
||||
DriverStationSim.setFmsAttached(fms_attached)
|
||||
DriverStationSim.notifyNewData()
|
||||
DriverStationBackend.silenceJoystickConnectionWarning(silenced)
|
||||
|
||||
joystick = Joystick(0)
|
||||
joystick.getRawButton(1)
|
||||
|
||||
stepTiming(1.0)
|
||||
assert (
|
||||
DriverStationBackend.isJoystickConnectionWarningSilenced() == expected_silenced
|
||||
)
|
||||
|
||||
# Capture stderr to check for warnings
|
||||
captured = capsys.readouterr()
|
||||
assert expected_warning in captured.err
|
||||
@@ -4,6 +4,11 @@ from wpilib import Joystick
|
||||
from wpilib.simulation import JoystickSim
|
||||
|
||||
|
||||
def test_fast_deconstruction(wpilib_state):
|
||||
# https://github.com/wpilibsuite/allwpilib/issues/1550
|
||||
Joystick(0)
|
||||
|
||||
|
||||
def test_getX() -> None:
|
||||
joy = Joystick(1)
|
||||
joysim = JoystickSim(joy)
|
||||
@@ -0,0 +1,37 @@
|
||||
from wpilib import NiDsPS4Controller
|
||||
from wpilib.simulation import NiDsPS4ControllerSim
|
||||
from driverstation.joystick_test_macros import button_test, axis_test
|
||||
|
||||
|
||||
def test_buttons():
|
||||
def ps4_button_test(btn_name):
|
||||
button_test(NiDsPS4Controller, NiDsPS4ControllerSim, btn_name)
|
||||
|
||||
def ps4_axis_test(axis_name):
|
||||
axis_test(NiDsPS4Controller, NiDsPS4ControllerSim, axis_name)
|
||||
|
||||
ps4_button_test("SquareButton")
|
||||
ps4_button_test("CrossButton")
|
||||
ps4_button_test("CircleButton")
|
||||
ps4_button_test("TriangleButton")
|
||||
|
||||
ps4_button_test("L1Button")
|
||||
ps4_button_test("R1Button")
|
||||
ps4_button_test("L2Button")
|
||||
ps4_button_test("R2Button")
|
||||
|
||||
ps4_button_test("ShareButton")
|
||||
ps4_button_test("OptionsButton")
|
||||
|
||||
ps4_button_test("L3Button")
|
||||
ps4_button_test("R3Button")
|
||||
|
||||
ps4_button_test("PSButton")
|
||||
ps4_button_test("TouchpadButton")
|
||||
|
||||
ps4_axis_test("LeftX")
|
||||
ps4_axis_test("RightX")
|
||||
ps4_axis_test("LeftY")
|
||||
ps4_axis_test("RightY")
|
||||
ps4_axis_test("L2Axis")
|
||||
ps4_axis_test("R2Axis")
|
||||
@@ -0,0 +1,37 @@
|
||||
from wpilib import NiDsPS5Controller
|
||||
from wpilib.simulation import NiDsPS5ControllerSim
|
||||
from driverstation.joystick_test_macros import button_test, axis_test
|
||||
|
||||
|
||||
def test_buttons():
|
||||
def ps5_button_test(btn_name):
|
||||
button_test(NiDsPS5Controller, NiDsPS5ControllerSim, btn_name)
|
||||
|
||||
def ps5_axis_test(axis_name):
|
||||
axis_test(NiDsPS5Controller, NiDsPS5ControllerSim, axis_name)
|
||||
|
||||
ps5_button_test("SquareButton")
|
||||
ps5_button_test("CrossButton")
|
||||
ps5_button_test("CircleButton")
|
||||
ps5_button_test("TriangleButton")
|
||||
|
||||
ps5_button_test("L1Button")
|
||||
ps5_button_test("R1Button")
|
||||
ps5_button_test("L2Button")
|
||||
ps5_button_test("R2Button")
|
||||
|
||||
ps5_button_test("CreateButton")
|
||||
ps5_button_test("OptionsButton")
|
||||
|
||||
ps5_button_test("L3Button")
|
||||
ps5_button_test("R3Button")
|
||||
|
||||
ps5_button_test("PSButton")
|
||||
ps5_button_test("TouchpadButton")
|
||||
|
||||
ps5_axis_test("LeftX")
|
||||
ps5_axis_test("RightX")
|
||||
ps5_axis_test("LeftY")
|
||||
ps5_axis_test("RightY")
|
||||
ps5_axis_test("L2Axis")
|
||||
ps5_axis_test("R2Axis")
|
||||
@@ -0,0 +1,32 @@
|
||||
from wpilib import NiDsXboxController
|
||||
from wpilib.simulation import NiDsXboxControllerSim
|
||||
from driverstation.joystick_test_macros import button_test, axis_test
|
||||
|
||||
|
||||
def test_buttons():
|
||||
def xbox_button_test(btn_name):
|
||||
button_test(NiDsXboxController, NiDsXboxControllerSim, btn_name)
|
||||
|
||||
def xbox_axis_test(axis_name):
|
||||
axis_test(NiDsXboxController, NiDsXboxControllerSim, axis_name)
|
||||
|
||||
xbox_button_test("LeftBumperButton")
|
||||
xbox_button_test("RightBumperButton")
|
||||
|
||||
xbox_button_test("LeftStickButton")
|
||||
xbox_button_test("RightStickButton")
|
||||
|
||||
xbox_button_test("AButton")
|
||||
xbox_button_test("BButton")
|
||||
xbox_button_test("XButton")
|
||||
xbox_button_test("YButton")
|
||||
xbox_button_test("BackButton")
|
||||
xbox_button_test("StartButton")
|
||||
|
||||
xbox_axis_test("LeftX")
|
||||
xbox_axis_test("RightX")
|
||||
xbox_axis_test("LeftY")
|
||||
xbox_axis_test("RightY")
|
||||
|
||||
xbox_axis_test("LeftTriggerAxis")
|
||||
xbox_axis_test("RightTriggerAxis")
|
||||
23
wpilibc/src/test/python/event/test_event_loop.py
Normal file
23
wpilibc/src/test/python/event/test_event_loop.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import EventLoop
|
||||
|
||||
|
||||
def test_concurrent_modification():
|
||||
loop = EventLoop()
|
||||
|
||||
def bind_during_poll():
|
||||
with pytest.raises(RuntimeError):
|
||||
loop.bind(lambda: None)
|
||||
|
||||
loop.bind(bind_during_poll)
|
||||
loop.poll()
|
||||
|
||||
loop.clear()
|
||||
|
||||
def clear_during_poll():
|
||||
with pytest.raises(RuntimeError):
|
||||
loop.clear()
|
||||
|
||||
loop.bind(clear_during_poll)
|
||||
loop.poll()
|
||||
41
wpilibc/src/test/python/event/test_network_boolean_event.py
Normal file
41
wpilibc/src/test/python/event/test_network_boolean_event.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import EventLoop, NetworkBooleanEvent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def nt_inst(wpilib_state):
|
||||
import ntcore
|
||||
|
||||
inst = ntcore.NetworkTableInstance.getDefault()
|
||||
inst.startLocal()
|
||||
try:
|
||||
yield inst
|
||||
finally:
|
||||
inst.stopLocal()
|
||||
inst._reset()
|
||||
|
||||
|
||||
def test_set(nt_inst):
|
||||
loop = EventLoop()
|
||||
counter = 0
|
||||
|
||||
def on_high() -> None:
|
||||
nonlocal counter
|
||||
counter += 1
|
||||
|
||||
pub = nt_inst.getTable("TestTable").getBooleanTopic("Test").publish()
|
||||
|
||||
NetworkBooleanEvent(loop, nt_inst, "TestTable", "Test").ifHigh(on_high)
|
||||
|
||||
pub.set(False)
|
||||
loop.poll()
|
||||
assert counter == 0
|
||||
|
||||
pub.set(True)
|
||||
loop.poll()
|
||||
assert counter == 1
|
||||
|
||||
pub.set(False)
|
||||
loop.poll()
|
||||
assert counter == 1
|
||||
@@ -1,7 +1,6 @@
|
||||
import pytest
|
||||
import threading
|
||||
from wpilib import simulation as wsim
|
||||
from wpimath.units import seconds
|
||||
from wpilib.opmoderobot import OpModeRobot
|
||||
from wpilib import OpMode, RobotState
|
||||
from hal._wpiHal import RobotMode
|
||||
400
wpilibc/src/test/python/framework/test_timed_robot.py
Normal file
400
wpilibc/src/test/python/framework/test_timed_robot.py
Normal file
@@ -0,0 +1,400 @@
|
||||
import threading
|
||||
|
||||
import pytest
|
||||
|
||||
from wpilib import TimedRobot
|
||||
from wpilib.simulation import (
|
||||
DriverStationSim,
|
||||
pauseTiming,
|
||||
resumeTiming,
|
||||
setProgramStarted,
|
||||
stepTiming,
|
||||
waitForProgramStart,
|
||||
)
|
||||
from hal import RobotMode
|
||||
|
||||
_PERIOD = 0.02 # 20 ms
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def timed_robot_setup(wpilib_state):
|
||||
pauseTiming()
|
||||
setProgramStarted(False)
|
||||
yield
|
||||
resumeTiming()
|
||||
|
||||
|
||||
class MockRobot(TimedRobot):
|
||||
def __init__(self):
|
||||
super().__init__(_PERIOD)
|
||||
self.simulation_init_count = 0
|
||||
self.disabled_init_count = 0
|
||||
self.autonomous_init_count = 0
|
||||
self.teleop_init_count = 0
|
||||
self.utility_init_count = 0
|
||||
|
||||
self.disabled_exit_count = 0
|
||||
self.autonomous_exit_count = 0
|
||||
self.teleop_exit_count = 0
|
||||
self.utility_exit_count = 0
|
||||
|
||||
self.robot_periodic_count = 0
|
||||
self.simulation_periodic_count = 0
|
||||
self.disabled_periodic_count = 0
|
||||
self.autonomous_periodic_count = 0
|
||||
self.teleop_periodic_count = 0
|
||||
self.utility_periodic_count = 0
|
||||
|
||||
def simulationInit(self):
|
||||
self.simulation_init_count += 1
|
||||
|
||||
def disabledInit(self):
|
||||
self.disabled_init_count += 1
|
||||
|
||||
def autonomousInit(self):
|
||||
self.autonomous_init_count += 1
|
||||
|
||||
def teleopInit(self):
|
||||
self.teleop_init_count += 1
|
||||
|
||||
def utilityInit(self):
|
||||
self.utility_init_count += 1
|
||||
|
||||
def robotPeriodic(self):
|
||||
self.robot_periodic_count += 1
|
||||
|
||||
def simulationPeriodic(self):
|
||||
self.simulation_periodic_count += 1
|
||||
|
||||
def disabledPeriodic(self):
|
||||
self.disabled_periodic_count += 1
|
||||
|
||||
def autonomousPeriodic(self):
|
||||
self.autonomous_periodic_count += 1
|
||||
|
||||
def teleopPeriodic(self):
|
||||
self.teleop_periodic_count += 1
|
||||
|
||||
def utilityPeriodic(self):
|
||||
self.utility_periodic_count += 1
|
||||
|
||||
def disabledExit(self):
|
||||
self.disabled_exit_count += 1
|
||||
|
||||
def autonomousExit(self):
|
||||
self.autonomous_exit_count += 1
|
||||
|
||||
def teleopExit(self):
|
||||
self.teleop_exit_count += 1
|
||||
|
||||
def utilityExit(self):
|
||||
self.utility_exit_count += 1
|
||||
|
||||
|
||||
def test_disabled_mode():
|
||||
robot = MockRobot()
|
||||
robot_thread = threading.Thread(target=robot.startCompetition, daemon=True)
|
||||
robot_thread.start()
|
||||
waitForProgramStart()
|
||||
|
||||
DriverStationSim.setEnabled(False)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
assert robot.simulation_init_count == 1
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.robot_periodic_count == 0
|
||||
assert robot.simulation_periodic_count == 0
|
||||
assert robot.disabled_periodic_count == 0
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.simulation_init_count == 1
|
||||
assert robot.disabled_init_count == 1
|
||||
assert robot.autonomous_init_count == 0
|
||||
assert robot.teleop_init_count == 0
|
||||
assert robot.utility_init_count == 0
|
||||
|
||||
assert robot.robot_periodic_count == 1
|
||||
assert robot.simulation_periodic_count == 1
|
||||
assert robot.disabled_periodic_count == 1
|
||||
assert robot.autonomous_periodic_count == 0
|
||||
assert robot.teleop_periodic_count == 0
|
||||
assert robot.utility_periodic_count == 0
|
||||
|
||||
assert robot.disabled_exit_count == 0
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.robot_periodic_count == 2
|
||||
assert robot.simulation_periodic_count == 2
|
||||
assert robot.disabled_periodic_count == 2
|
||||
|
||||
robot.endCompetition()
|
||||
robot_thread.join()
|
||||
|
||||
|
||||
def test_autonomous_mode():
|
||||
robot = MockRobot()
|
||||
robot_thread = threading.Thread(target=robot.startCompetition, daemon=True)
|
||||
robot_thread.start()
|
||||
waitForProgramStart()
|
||||
|
||||
DriverStationSim.setEnabled(True)
|
||||
DriverStationSim.setRobotMode(RobotMode.AUTONOMOUS)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
assert robot.simulation_init_count == 1
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.autonomous_init_count == 0
|
||||
assert robot.robot_periodic_count == 0
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.simulation_init_count == 1
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.autonomous_init_count == 1
|
||||
assert robot.teleop_init_count == 0
|
||||
assert robot.utility_init_count == 0
|
||||
|
||||
assert robot.robot_periodic_count == 1
|
||||
assert robot.simulation_periodic_count == 1
|
||||
assert robot.disabled_periodic_count == 0
|
||||
assert robot.autonomous_periodic_count == 1
|
||||
assert robot.teleop_periodic_count == 0
|
||||
assert robot.utility_periodic_count == 0
|
||||
|
||||
assert robot.autonomous_exit_count == 0
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.robot_periodic_count == 2
|
||||
assert robot.simulation_periodic_count == 2
|
||||
assert robot.autonomous_periodic_count == 2
|
||||
|
||||
robot.endCompetition()
|
||||
robot_thread.join()
|
||||
|
||||
|
||||
def test_teleop_mode():
|
||||
robot = MockRobot()
|
||||
robot_thread = threading.Thread(target=robot.startCompetition, daemon=True)
|
||||
robot_thread.start()
|
||||
waitForProgramStart()
|
||||
|
||||
DriverStationSim.setEnabled(True)
|
||||
DriverStationSim.setRobotMode(RobotMode.TELEOPERATED)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
assert robot.simulation_init_count == 1
|
||||
assert robot.teleop_init_count == 0
|
||||
assert robot.robot_periodic_count == 0
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.autonomous_init_count == 0
|
||||
assert robot.teleop_init_count == 1
|
||||
assert robot.utility_init_count == 0
|
||||
|
||||
assert robot.robot_periodic_count == 1
|
||||
assert robot.simulation_periodic_count == 1
|
||||
assert robot.disabled_periodic_count == 0
|
||||
assert robot.autonomous_periodic_count == 0
|
||||
assert robot.teleop_periodic_count == 1
|
||||
assert robot.utility_periodic_count == 0
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.robot_periodic_count == 2
|
||||
assert robot.teleop_periodic_count == 2
|
||||
|
||||
robot.endCompetition()
|
||||
robot_thread.join()
|
||||
|
||||
|
||||
def test_utility_mode():
|
||||
robot = MockRobot()
|
||||
robot_thread = threading.Thread(target=robot.startCompetition, daemon=True)
|
||||
robot_thread.start()
|
||||
waitForProgramStart()
|
||||
|
||||
DriverStationSim.setEnabled(True)
|
||||
DriverStationSim.setRobotMode(RobotMode.UTILITY)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
assert robot.simulation_init_count == 1
|
||||
assert robot.utility_init_count == 0
|
||||
assert robot.robot_periodic_count == 0
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.utility_init_count == 1
|
||||
assert robot.robot_periodic_count == 1
|
||||
assert robot.utility_periodic_count == 1
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.robot_periodic_count == 2
|
||||
assert robot.utility_periodic_count == 2
|
||||
|
||||
DriverStationSim.setEnabled(False)
|
||||
DriverStationSim.notifyNewData()
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.disabled_init_count == 1
|
||||
assert robot.robot_periodic_count == 3
|
||||
assert robot.disabled_periodic_count == 1
|
||||
assert robot.utility_exit_count == 1
|
||||
|
||||
robot.endCompetition()
|
||||
robot_thread.join()
|
||||
|
||||
|
||||
def test_mode_change():
|
||||
robot = MockRobot()
|
||||
robot_thread = threading.Thread(target=robot.startCompetition, daemon=True)
|
||||
robot_thread.start()
|
||||
waitForProgramStart()
|
||||
|
||||
DriverStationSim.setEnabled(False)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.disabled_exit_count == 0
|
||||
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.disabled_init_count == 1
|
||||
assert robot.disabled_exit_count == 0
|
||||
|
||||
DriverStationSim.setEnabled(True)
|
||||
DriverStationSim.setRobotMode(RobotMode.AUTONOMOUS)
|
||||
DriverStationSim.notifyNewData()
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.disabled_init_count == 1
|
||||
assert robot.autonomous_init_count == 1
|
||||
assert robot.teleop_init_count == 0
|
||||
assert robot.utility_init_count == 0
|
||||
assert robot.disabled_exit_count == 1
|
||||
assert robot.autonomous_exit_count == 0
|
||||
|
||||
DriverStationSim.setRobotMode(RobotMode.TELEOPERATED)
|
||||
DriverStationSim.notifyNewData()
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.autonomous_init_count == 1
|
||||
assert robot.teleop_init_count == 1
|
||||
assert robot.utility_init_count == 0
|
||||
assert robot.autonomous_exit_count == 1
|
||||
assert robot.teleop_exit_count == 0
|
||||
|
||||
DriverStationSim.setRobotMode(RobotMode.UTILITY)
|
||||
DriverStationSim.notifyNewData()
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.utility_init_count == 1
|
||||
assert robot.teleop_exit_count == 1
|
||||
assert robot.utility_exit_count == 0
|
||||
|
||||
DriverStationSim.setEnabled(False)
|
||||
DriverStationSim.notifyNewData()
|
||||
stepTiming(_PERIOD)
|
||||
|
||||
assert robot.disabled_init_count == 2
|
||||
assert robot.autonomous_init_count == 1
|
||||
assert robot.teleop_init_count == 1
|
||||
assert robot.utility_init_count == 1
|
||||
assert robot.disabled_exit_count == 1
|
||||
assert robot.autonomous_exit_count == 1
|
||||
assert robot.teleop_exit_count == 1
|
||||
assert robot.utility_exit_count == 1
|
||||
|
||||
robot.endCompetition()
|
||||
robot_thread.join()
|
||||
|
||||
|
||||
def test_add_periodic():
|
||||
robot = MockRobot()
|
||||
callback_count = 0
|
||||
|
||||
def on_periodic() -> None:
|
||||
nonlocal callback_count
|
||||
callback_count += 1
|
||||
|
||||
robot.addPeriodic(on_periodic, _PERIOD / 2.0)
|
||||
|
||||
robot_thread = threading.Thread(target=robot.startCompetition, daemon=True)
|
||||
robot_thread.start()
|
||||
waitForProgramStart()
|
||||
|
||||
DriverStationSim.setEnabled(False)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.disabled_periodic_count == 0
|
||||
assert callback_count == 0
|
||||
|
||||
stepTiming(_PERIOD / 2.0)
|
||||
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.disabled_periodic_count == 0
|
||||
assert callback_count == 1
|
||||
|
||||
stepTiming(_PERIOD / 2.0)
|
||||
|
||||
assert robot.disabled_init_count == 1
|
||||
assert robot.disabled_periodic_count == 1
|
||||
assert callback_count == 2
|
||||
|
||||
robot.endCompetition()
|
||||
robot_thread.join()
|
||||
|
||||
|
||||
def test_add_periodic_with_offset():
|
||||
robot = MockRobot()
|
||||
callback_count = 0
|
||||
|
||||
def on_periodic() -> None:
|
||||
nonlocal callback_count
|
||||
callback_count += 1
|
||||
|
||||
robot.addPeriodic(on_periodic, _PERIOD / 2.0, _PERIOD / 4.0)
|
||||
|
||||
robot_thread = threading.Thread(target=robot.startCompetition, daemon=True)
|
||||
robot_thread.start()
|
||||
waitForProgramStart()
|
||||
|
||||
DriverStationSim.setEnabled(False)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.disabled_periodic_count == 0
|
||||
assert callback_count == 0
|
||||
|
||||
stepTiming(_PERIOD * 3.0 / 8.0)
|
||||
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.disabled_periodic_count == 0
|
||||
assert callback_count == 0
|
||||
|
||||
stepTiming(_PERIOD * 3.0 / 8.0)
|
||||
|
||||
assert robot.disabled_init_count == 0
|
||||
assert robot.disabled_periodic_count == 0
|
||||
assert callback_count == 1
|
||||
|
||||
stepTiming(_PERIOD / 4.0)
|
||||
|
||||
assert robot.disabled_init_count == 1
|
||||
assert robot.disabled_periodic_count == 1
|
||||
assert callback_count == 1
|
||||
|
||||
stepTiming(_PERIOD / 4.0)
|
||||
|
||||
assert robot.disabled_init_count == 1
|
||||
assert robot.disabled_periodic_count == 1
|
||||
assert callback_count == 2
|
||||
|
||||
robot.endCompetition()
|
||||
robot_thread.join()
|
||||
95
wpilibc/src/test/python/framework/test_timeslice_robot.py
Normal file
95
wpilibc/src/test/python/framework/test_timeslice_robot.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import threading
|
||||
|
||||
import pytest
|
||||
|
||||
from wpilib import TimesliceRobot
|
||||
from wpilib.simulation import (
|
||||
DriverStationSim,
|
||||
pauseTiming,
|
||||
resumeTiming,
|
||||
setProgramStarted,
|
||||
stepTiming,
|
||||
waitForProgramStart,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def timeslice_setup(wpilib_state):
|
||||
pauseTiming()
|
||||
setProgramStarted(False)
|
||||
yield
|
||||
resumeTiming()
|
||||
|
||||
|
||||
class MockRobot(TimesliceRobot):
|
||||
def __init__(self):
|
||||
super().__init__(0.002, 0.005)
|
||||
self.robot_periodic_count = 0
|
||||
|
||||
def robotPeriodic(self):
|
||||
self.robot_periodic_count += 1
|
||||
|
||||
|
||||
def test_schedule():
|
||||
robot = MockRobot()
|
||||
|
||||
callback_count1 = 0
|
||||
callback_count2 = 0
|
||||
|
||||
def callback1() -> None:
|
||||
nonlocal callback_count1
|
||||
callback_count1 += 1
|
||||
|
||||
def callback2() -> None:
|
||||
nonlocal callback_count2
|
||||
callback_count2 += 1
|
||||
|
||||
robot.schedule(callback1, 0.0005)
|
||||
robot.schedule(callback2, 0.001)
|
||||
|
||||
robot_thread = threading.Thread(target=robot.startCompetition, daemon=True)
|
||||
robot_thread.start()
|
||||
waitForProgramStart()
|
||||
|
||||
DriverStationSim.setEnabled(False)
|
||||
DriverStationSim.notifyNewData()
|
||||
|
||||
# First 5 ms: no callbacks fired yet (delayed by one period)
|
||||
stepTiming(0.005)
|
||||
assert robot.robot_periodic_count == 0
|
||||
assert callback_count1 == 0
|
||||
assert callback_count2 == 0
|
||||
|
||||
# Step to 1.5 ms into next period — nothing yet
|
||||
stepTiming(0.0015)
|
||||
assert robot.robot_periodic_count == 0
|
||||
assert callback_count1 == 0
|
||||
assert callback_count2 == 0
|
||||
|
||||
# Step to 2.25 ms — callback1 fires (offset 2 ms, period 0.5 ms)
|
||||
stepTiming(0.00075)
|
||||
assert robot.robot_periodic_count == 0
|
||||
assert callback_count1 == 1
|
||||
assert callback_count2 == 0
|
||||
|
||||
# Step to 2.75 ms — callback2 fires (offset 2.5 ms, period 1 ms)
|
||||
stepTiming(0.0005)
|
||||
assert robot.robot_periodic_count == 0
|
||||
assert callback_count1 == 1
|
||||
assert callback_count2 == 1
|
||||
|
||||
robot.endCompetition()
|
||||
robot_thread.join()
|
||||
|
||||
|
||||
def test_schedule_overrun():
|
||||
robot = MockRobot()
|
||||
|
||||
robot.schedule(lambda: None, 0.0005)
|
||||
robot.schedule(lambda: None, 0.001)
|
||||
|
||||
# offset = 2 ms + 0.5 ms + 1 ms = 3.5 ms; 3.5 ms + 3 ms = 6.5 ms > 5 ms max
|
||||
with pytest.raises(RuntimeError):
|
||||
robot.schedule(lambda: None, 0.003)
|
||||
|
||||
robot.endCompetition()
|
||||
@@ -0,0 +1,115 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import DoubleSolenoid, PneumaticsModuleType, Solenoid
|
||||
|
||||
|
||||
def test_valid_initialization_ctre(wpilib_state):
|
||||
solenoid = DoubleSolenoid(0, 3, PneumaticsModuleType.CTRE_PCM, 2, 3)
|
||||
solenoid.set(DoubleSolenoid.Value.REVERSE)
|
||||
assert solenoid.get() == DoubleSolenoid.Value.REVERSE
|
||||
|
||||
solenoid.set(DoubleSolenoid.Value.FORWARD)
|
||||
assert solenoid.get() == DoubleSolenoid.Value.FORWARD
|
||||
|
||||
solenoid.set(DoubleSolenoid.Value.OFF)
|
||||
assert solenoid.get() == DoubleSolenoid.Value.OFF
|
||||
|
||||
|
||||
def test_throw_forward_port_already_initialized_ctre(wpilib_state):
|
||||
s = Solenoid(0, 5, PneumaticsModuleType.CTRE_PCM, 2)
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 5, PneumaticsModuleType.CTRE_PCM, 2, 3)
|
||||
|
||||
|
||||
def test_throw_reverse_port_already_initialized_ctre(wpilib_state):
|
||||
s = Solenoid(0, 6, PneumaticsModuleType.CTRE_PCM, 3)
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 6, PneumaticsModuleType.CTRE_PCM, 2, 3)
|
||||
|
||||
|
||||
def test_throw_both_ports_already_initialized_ctre(wpilib_state):
|
||||
s0 = Solenoid(0, 6, PneumaticsModuleType.CTRE_PCM, 2)
|
||||
s1 = Solenoid(0, 6, PneumaticsModuleType.CTRE_PCM, 3)
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 6, PneumaticsModuleType.CTRE_PCM, 2, 3)
|
||||
|
||||
|
||||
def test_toggle_ctre(wpilib_state):
|
||||
solenoid = DoubleSolenoid(0, 4, PneumaticsModuleType.CTRE_PCM, 2, 3)
|
||||
solenoid.set(DoubleSolenoid.Value.REVERSE)
|
||||
|
||||
solenoid.toggle()
|
||||
assert solenoid.get() == DoubleSolenoid.Value.FORWARD
|
||||
|
||||
solenoid.toggle()
|
||||
assert solenoid.get() == DoubleSolenoid.Value.REVERSE
|
||||
|
||||
solenoid.set(DoubleSolenoid.Value.OFF)
|
||||
solenoid.toggle()
|
||||
assert solenoid.get() == DoubleSolenoid.Value.OFF
|
||||
|
||||
|
||||
def test_invalid_forward_port_ctre(wpilib_state):
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 0, PneumaticsModuleType.CTRE_PCM, 100, 1)
|
||||
|
||||
|
||||
def test_invalid_reverse_port_ctre(wpilib_state):
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 0, PneumaticsModuleType.CTRE_PCM, 0, 100)
|
||||
|
||||
|
||||
def test_valid_initialization_rev(wpilib_state):
|
||||
solenoid = DoubleSolenoid(0, 3, PneumaticsModuleType.REV_PH, 2, 3)
|
||||
solenoid.set(DoubleSolenoid.Value.REVERSE)
|
||||
assert solenoid.get() == DoubleSolenoid.Value.REVERSE
|
||||
|
||||
solenoid.set(DoubleSolenoid.Value.FORWARD)
|
||||
assert solenoid.get() == DoubleSolenoid.Value.FORWARD
|
||||
|
||||
solenoid.set(DoubleSolenoid.Value.OFF)
|
||||
assert solenoid.get() == DoubleSolenoid.Value.OFF
|
||||
|
||||
|
||||
def test_throw_forward_port_already_initialized_rev(wpilib_state):
|
||||
s = Solenoid(0, 5, PneumaticsModuleType.REV_PH, 2)
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 5, PneumaticsModuleType.REV_PH, 2, 3)
|
||||
|
||||
|
||||
def test_throw_reverse_port_already_initialized_rev(wpilib_state):
|
||||
s = Solenoid(0, 6, PneumaticsModuleType.REV_PH, 3)
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 6, PneumaticsModuleType.REV_PH, 2, 3)
|
||||
|
||||
|
||||
def test_throw_both_ports_already_initialized_rev(wpilib_state):
|
||||
s0 = Solenoid(0, 6, PneumaticsModuleType.REV_PH, 2)
|
||||
s1 = Solenoid(0, 6, PneumaticsModuleType.REV_PH, 3)
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 6, PneumaticsModuleType.REV_PH, 2, 3)
|
||||
|
||||
|
||||
def test_toggle_rev(wpilib_state):
|
||||
solenoid = DoubleSolenoid(0, 4, PneumaticsModuleType.REV_PH, 2, 3)
|
||||
solenoid.set(DoubleSolenoid.Value.REVERSE)
|
||||
|
||||
solenoid.toggle()
|
||||
assert solenoid.get() == DoubleSolenoid.Value.FORWARD
|
||||
|
||||
solenoid.toggle()
|
||||
assert solenoid.get() == DoubleSolenoid.Value.REVERSE
|
||||
|
||||
solenoid.set(DoubleSolenoid.Value.OFF)
|
||||
solenoid.toggle()
|
||||
assert solenoid.get() == DoubleSolenoid.Value.OFF
|
||||
|
||||
|
||||
def test_invalid_forward_port_rev(wpilib_state):
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 0, PneumaticsModuleType.REV_PH, 100, 1)
|
||||
|
||||
|
||||
def test_invalid_reverse_port_rev(wpilib_state):
|
||||
with pytest.raises(RuntimeError):
|
||||
DoubleSolenoid(0, 0, PneumaticsModuleType.REV_PH, 0, 100)
|
||||
83
wpilibc/src/test/python/hardware/pneumatic/test_solenoid.py
Normal file
83
wpilibc/src/test/python/hardware/pneumatic/test_solenoid.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import DoubleSolenoid, PneumaticsModuleType, Solenoid
|
||||
|
||||
|
||||
def test_valid_initialization_ctre(wpilib_state):
|
||||
solenoid = Solenoid(0, 3, PneumaticsModuleType.CTRE_PCM, 2)
|
||||
assert solenoid.getChannel() == 2
|
||||
|
||||
solenoid.set(True)
|
||||
assert solenoid.get()
|
||||
|
||||
solenoid.set(False)
|
||||
assert not solenoid.get()
|
||||
|
||||
|
||||
def test_double_initialization_ctre(wpilib_state):
|
||||
s = Solenoid(0, 3, PneumaticsModuleType.CTRE_PCM, 2)
|
||||
with pytest.raises(RuntimeError):
|
||||
Solenoid(0, 3, PneumaticsModuleType.CTRE_PCM, 2)
|
||||
|
||||
|
||||
def test_double_initialization_from_double_solenoid_ctre(wpilib_state):
|
||||
ds = DoubleSolenoid(0, 3, PneumaticsModuleType.CTRE_PCM, 2, 3)
|
||||
with pytest.raises(RuntimeError):
|
||||
Solenoid(0, 3, PneumaticsModuleType.CTRE_PCM, 2)
|
||||
|
||||
|
||||
def test_invalid_channel_ctre(wpilib_state):
|
||||
with pytest.raises(RuntimeError):
|
||||
Solenoid(0, 3, PneumaticsModuleType.CTRE_PCM, 100)
|
||||
|
||||
|
||||
def test_toggle_ctre(wpilib_state):
|
||||
solenoid = Solenoid(0, 3, PneumaticsModuleType.CTRE_PCM, 2)
|
||||
solenoid.set(True)
|
||||
assert solenoid.get()
|
||||
|
||||
solenoid.toggle()
|
||||
assert not solenoid.get()
|
||||
|
||||
solenoid.toggle()
|
||||
assert solenoid.get()
|
||||
|
||||
|
||||
def test_valid_initialization_rev(wpilib_state):
|
||||
solenoid = Solenoid(0, 3, PneumaticsModuleType.REV_PH, 2)
|
||||
assert solenoid.getChannel() == 2
|
||||
|
||||
solenoid.set(True)
|
||||
assert solenoid.get()
|
||||
|
||||
solenoid.set(False)
|
||||
assert not solenoid.get()
|
||||
|
||||
|
||||
def test_double_initialization_rev(wpilib_state):
|
||||
s = Solenoid(0, 3, PneumaticsModuleType.REV_PH, 2)
|
||||
with pytest.raises(RuntimeError):
|
||||
Solenoid(0, 3, PneumaticsModuleType.REV_PH, 2)
|
||||
|
||||
|
||||
def test_double_initialization_from_double_solenoid_rev(wpilib_state):
|
||||
ds = DoubleSolenoid(0, 3, PneumaticsModuleType.REV_PH, 2, 3)
|
||||
with pytest.raises(RuntimeError):
|
||||
Solenoid(0, 3, PneumaticsModuleType.REV_PH, 2)
|
||||
|
||||
|
||||
def test_invalid_channel_rev(wpilib_state):
|
||||
with pytest.raises(RuntimeError):
|
||||
Solenoid(0, 3, PneumaticsModuleType.REV_PH, 100)
|
||||
|
||||
|
||||
def test_toggle_rev(wpilib_state):
|
||||
solenoid = Solenoid(0, 3, PneumaticsModuleType.REV_PH, 2)
|
||||
solenoid.set(True)
|
||||
assert solenoid.get()
|
||||
|
||||
solenoid.toggle()
|
||||
assert not solenoid.get()
|
||||
|
||||
solenoid.toggle()
|
||||
assert solenoid.get()
|
||||
18
wpilibc/src/test/python/hardware/range/test_sharp_ir.py
Normal file
18
wpilibc/src/test/python/hardware/range/test_sharp_ir.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import SharpIR
|
||||
from wpilib.simulation import SharpIRSim
|
||||
|
||||
|
||||
def test_sim_devices(wpilib_state):
|
||||
s = SharpIR.GP2Y0A02YK0F(1)
|
||||
sim = SharpIRSim(s)
|
||||
|
||||
assert s.getRange() == pytest.approx(0.2)
|
||||
|
||||
sim.setRange(0.3)
|
||||
assert s.getRange() == pytest.approx(0.3)
|
||||
|
||||
# Clamped to max range of 1.5 m for GP2Y0A02YK0F
|
||||
sim.setRange(3.0)
|
||||
assert s.getRange() == pytest.approx(1.5)
|
||||
@@ -0,0 +1,75 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import AnalogInput, AnalogPotentiometer
|
||||
from wpilib.simulation import AnalogInputSim, RoboRioSim
|
||||
|
||||
|
||||
def test_initialize_with_analog_input(wpilib_state):
|
||||
ai = AnalogInput(0)
|
||||
pot = AnalogPotentiometer(ai)
|
||||
sim = AnalogInputSim(ai)
|
||||
|
||||
RoboRioSim.resetData()
|
||||
sim.setVoltage(2.8)
|
||||
assert pot.get() == pytest.approx(2.8 / 3.3)
|
||||
|
||||
|
||||
def test_initialize_with_analog_input_and_scale(wpilib_state):
|
||||
ai = AnalogInput(0)
|
||||
pot = AnalogPotentiometer(ai, 270.0)
|
||||
RoboRioSim.resetData()
|
||||
sim = AnalogInputSim(ai)
|
||||
|
||||
sim.setVoltage(3.3)
|
||||
assert pot.get() == pytest.approx(270.0)
|
||||
|
||||
sim.setVoltage(2.5)
|
||||
assert pot.get() == pytest.approx((2.5 / 3.3) * 270.0)
|
||||
|
||||
sim.setVoltage(0.0)
|
||||
assert pot.get() == pytest.approx(0.0)
|
||||
|
||||
|
||||
def test_initialize_with_channel(wpilib_state):
|
||||
pot = AnalogPotentiometer(1)
|
||||
sim = AnalogInputSim(1)
|
||||
|
||||
sim.setVoltage(3.3)
|
||||
assert pot.get() == pytest.approx(1.0)
|
||||
|
||||
|
||||
def test_initialize_with_channel_and_scale(wpilib_state):
|
||||
pot = AnalogPotentiometer(1, 180.0)
|
||||
RoboRioSim.resetData()
|
||||
sim = AnalogInputSim(1)
|
||||
|
||||
sim.setVoltage(3.3)
|
||||
assert pot.get() == pytest.approx(180.0)
|
||||
|
||||
sim.setVoltage(0.0)
|
||||
assert pot.get() == pytest.approx(0.0)
|
||||
|
||||
|
||||
def test_with_modified_battery_voltage(wpilib_state):
|
||||
pot = AnalogPotentiometer(1, 180.0, 90.0)
|
||||
RoboRioSim.resetData()
|
||||
sim = AnalogInputSim(1)
|
||||
|
||||
# Test at 3.3 V
|
||||
sim.setVoltage(3.3)
|
||||
assert pot.get() == pytest.approx(270.0)
|
||||
|
||||
sim.setVoltage(0.0)
|
||||
assert pot.get() == pytest.approx(90.0)
|
||||
|
||||
# Simulate lower battery voltage
|
||||
RoboRioSim.setUserVoltage3V3(2.5)
|
||||
|
||||
sim.setVoltage(2.5)
|
||||
assert pot.get() == pytest.approx(270.0)
|
||||
|
||||
sim.setVoltage(2.0)
|
||||
assert pot.get() == pytest.approx(234.0)
|
||||
|
||||
sim.setVoltage(0.0)
|
||||
assert pot.get() == pytest.approx(90.0)
|
||||
@@ -10,10 +10,9 @@ Level = Alert.Level
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def group_name(request):
|
||||
|
||||
AlertSim.resetData()
|
||||
group_name = f"AlertTest_{request.node.name}"
|
||||
yield group_name
|
||||
|
||||
AlertSim.resetData()
|
||||
|
||||
|
||||
89
wpilibc/src/test/python/smartdashboard/test_mechanism2d.py
Normal file
89
wpilibc/src/test/python/smartdashboard/test_mechanism2d.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import pytest
|
||||
|
||||
from ntcore import NetworkTableInstance
|
||||
from wpilib import Mechanism2d, MechanismLigament2d, MechanismRoot2d, SmartDashboard
|
||||
from wpiutil import Color8Bit
|
||||
|
||||
|
||||
def test_create_mechanism():
|
||||
m = Mechanism2d(100, 100)
|
||||
r1 = m.getRoot("r1", 10, 10)
|
||||
l1 = r1.appendLigament("l1", 4, 3)
|
||||
l2 = l1.appendLigament("l2", 4, 3)
|
||||
assert l2 is not None
|
||||
|
||||
|
||||
def test_canvas(nt: NetworkTableInstance):
|
||||
mechanism = Mechanism2d(5, 10)
|
||||
dims_entry = nt.getEntry("/SmartDashboard/mechanism/dims")
|
||||
color_entry = nt.getEntry("/SmartDashboard/mechanism/backgroundColor")
|
||||
|
||||
SmartDashboard.putData("mechanism", mechanism)
|
||||
SmartDashboard.updateValues()
|
||||
|
||||
dims = dims_entry.getDoubleArray([])
|
||||
assert dims[0] == pytest.approx(5.0)
|
||||
assert dims[1] == pytest.approx(10.0)
|
||||
assert color_entry.getString("") == "#000020"
|
||||
|
||||
mechanism.setBackgroundColor(Color8Bit(255, 255, 255))
|
||||
SmartDashboard.updateValues()
|
||||
assert color_entry.getString("") == "#FFFFFF"
|
||||
|
||||
|
||||
def test_root(nt: NetworkTableInstance):
|
||||
mechanism = Mechanism2d(5, 10)
|
||||
x_entry = nt.getEntry("/SmartDashboard/mechanism/root/x")
|
||||
y_entry = nt.getEntry("/SmartDashboard/mechanism/root/y")
|
||||
|
||||
root = mechanism.getRoot("root", 1, 2)
|
||||
SmartDashboard.putData("mechanism", mechanism)
|
||||
SmartDashboard.updateValues()
|
||||
|
||||
assert x_entry.getDouble(0.0) == pytest.approx(1.0)
|
||||
assert y_entry.getDouble(0.0) == pytest.approx(2.0)
|
||||
|
||||
root.setPosition(2, 4)
|
||||
SmartDashboard.updateValues()
|
||||
|
||||
assert x_entry.getDouble(0.0) == pytest.approx(2.0)
|
||||
assert y_entry.getDouble(0.0) == pytest.approx(4.0)
|
||||
|
||||
|
||||
def test_ligament(nt: NetworkTableInstance):
|
||||
mechanism = Mechanism2d(5, 10)
|
||||
angle_entry = nt.getEntry("/SmartDashboard/mechanism/root/ligament/angle")
|
||||
color_entry = nt.getEntry("/SmartDashboard/mechanism/root/ligament/color")
|
||||
length_entry = nt.getEntry("/SmartDashboard/mechanism/root/ligament/length")
|
||||
weight_entry = nt.getEntry("/SmartDashboard/mechanism/root/ligament/weight")
|
||||
|
||||
root = mechanism.getRoot("root", 1, 2)
|
||||
ligament = root.appendLigament("ligament", 3, 90, 1, Color8Bit(255, 255, 255))
|
||||
SmartDashboard.putData("mechanism", mechanism)
|
||||
|
||||
assert ligament.getAngle() == pytest.approx(angle_entry.getDouble(0.0))
|
||||
assert ligament.getColor().hexString() == color_entry.getString("")
|
||||
assert ligament.getLength() == pytest.approx(length_entry.getDouble(0.0))
|
||||
assert ligament.getLineWeight() == pytest.approx(weight_entry.getDouble(0.0))
|
||||
|
||||
ligament.setAngle(45)
|
||||
ligament.setColor(Color8Bit(0, 0, 0))
|
||||
ligament.setLength(2)
|
||||
ligament.setLineWeight(4)
|
||||
SmartDashboard.updateValues()
|
||||
|
||||
assert ligament.getAngle() == pytest.approx(angle_entry.getDouble(0.0))
|
||||
assert ligament.getColor().hexString() == color_entry.getString("")
|
||||
assert ligament.getLength() == pytest.approx(length_entry.getDouble(0.0))
|
||||
assert ligament.getLineWeight() == pytest.approx(weight_entry.getDouble(0.0))
|
||||
|
||||
angle_entry.setDouble(22.5)
|
||||
color_entry.setString("#FF00FF")
|
||||
length_entry.setDouble(4.0)
|
||||
weight_entry.setDouble(6.0)
|
||||
SmartDashboard.updateValues()
|
||||
|
||||
assert ligament.getAngle() == pytest.approx(angle_entry.getDouble(0.0))
|
||||
assert ligament.getColor().hexString() == color_entry.getString("")
|
||||
assert ligament.getLength() == pytest.approx(length_entry.getDouble(0.0))
|
||||
assert ligament.getLineWeight() == pytest.approx(weight_entry.getDouble(0.0))
|
||||
157
wpilibc/src/test/python/system/test_watchdog.py
Normal file
157
wpilibc/src/test/python/system/test_watchdog.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import pytest
|
||||
|
||||
from wpilib import Watchdog
|
||||
from wpilib.simulation import pauseTiming, resumeTiming, stepTiming
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def watchdog_setup():
|
||||
pauseTiming()
|
||||
yield
|
||||
resumeTiming()
|
||||
|
||||
|
||||
def test_enable_disable():
|
||||
counter = 0
|
||||
|
||||
def on_expire() -> None:
|
||||
nonlocal counter
|
||||
counter += 1
|
||||
|
||||
watchdog = Watchdog(0.4, on_expire)
|
||||
|
||||
# Run 1: disable before timeout
|
||||
watchdog.enable()
|
||||
stepTiming(0.2)
|
||||
watchdog.disable()
|
||||
assert counter == 0
|
||||
|
||||
# Run 2: step past timeout
|
||||
counter = 0
|
||||
watchdog.enable()
|
||||
stepTiming(0.4)
|
||||
watchdog.disable()
|
||||
assert counter == 1
|
||||
|
||||
# Run 3: step well past timeout, only triggers once
|
||||
counter = 0
|
||||
watchdog.enable()
|
||||
stepTiming(1.0)
|
||||
watchdog.disable()
|
||||
assert counter == 1
|
||||
|
||||
|
||||
def test_reset():
|
||||
counter = 0
|
||||
|
||||
def on_expire() -> None:
|
||||
nonlocal counter
|
||||
counter += 1
|
||||
|
||||
watchdog = Watchdog(0.4, on_expire)
|
||||
|
||||
watchdog.enable()
|
||||
stepTiming(0.2)
|
||||
watchdog.reset()
|
||||
stepTiming(0.2)
|
||||
watchdog.disable()
|
||||
|
||||
assert counter == 0
|
||||
|
||||
|
||||
def test_set_timeout():
|
||||
counter = 0
|
||||
|
||||
def on_expire() -> None:
|
||||
nonlocal counter
|
||||
counter += 1
|
||||
|
||||
watchdog = Watchdog(1.0, on_expire)
|
||||
|
||||
watchdog.enable()
|
||||
stepTiming(0.2)
|
||||
watchdog.setTimeout(0.2)
|
||||
|
||||
assert watchdog.getTimeout() == pytest.approx(0.2)
|
||||
assert counter == 0
|
||||
|
||||
stepTiming(0.3)
|
||||
watchdog.disable()
|
||||
|
||||
assert counter == 1
|
||||
|
||||
|
||||
def test_is_expired():
|
||||
watchdog = Watchdog(0.2, lambda: None)
|
||||
assert not watchdog.isExpired()
|
||||
watchdog.enable()
|
||||
|
||||
assert not watchdog.isExpired()
|
||||
stepTiming(0.3)
|
||||
assert watchdog.isExpired()
|
||||
|
||||
watchdog.disable()
|
||||
assert watchdog.isExpired()
|
||||
|
||||
watchdog.reset()
|
||||
assert not watchdog.isExpired()
|
||||
|
||||
|
||||
def test_epochs():
|
||||
counter = 0
|
||||
|
||||
def on_expire() -> None:
|
||||
nonlocal counter
|
||||
counter += 1
|
||||
|
||||
watchdog = Watchdog(0.4, on_expire)
|
||||
|
||||
# Run 1: under timeout with epochs
|
||||
watchdog.enable()
|
||||
watchdog.addEpoch("Epoch 1")
|
||||
stepTiming(0.1)
|
||||
watchdog.addEpoch("Epoch 2")
|
||||
stepTiming(0.1)
|
||||
watchdog.addEpoch("Epoch 3")
|
||||
watchdog.disable()
|
||||
assert counter == 0
|
||||
|
||||
# Run 2: reset mid-run keeps under timeout
|
||||
watchdog.enable()
|
||||
watchdog.addEpoch("Epoch 1")
|
||||
stepTiming(0.2)
|
||||
watchdog.reset()
|
||||
stepTiming(0.2)
|
||||
watchdog.addEpoch("Epoch 2")
|
||||
watchdog.disable()
|
||||
assert counter == 0
|
||||
|
||||
|
||||
def test_multi_watchdog():
|
||||
counter1 = 0
|
||||
counter2 = 0
|
||||
|
||||
def on_expire1() -> None:
|
||||
nonlocal counter1
|
||||
counter1 += 1
|
||||
|
||||
def on_expire2() -> None:
|
||||
nonlocal counter2
|
||||
counter2 += 1
|
||||
|
||||
watchdog1 = Watchdog(0.2, on_expire1)
|
||||
watchdog2 = Watchdog(0.6, on_expire2)
|
||||
|
||||
watchdog2.enable()
|
||||
stepTiming(0.25)
|
||||
assert counter1 == 0
|
||||
assert counter2 == 0
|
||||
|
||||
# watchdog1 enabled later but has shorter timeout — expires first
|
||||
watchdog1.enable()
|
||||
stepTiming(0.25)
|
||||
watchdog1.disable()
|
||||
watchdog2.disable()
|
||||
|
||||
assert counter1 == 1
|
||||
assert counter2 == 0
|
||||
@@ -1,11 +0,0 @@
|
||||
from wpilib import Mechanism2d
|
||||
|
||||
|
||||
def test_create_mechanism():
|
||||
m = Mechanism2d(100, 100)
|
||||
r1 = m.getRoot("r1", 10, 10)
|
||||
l1 = r1.appendLigament("l1", 4, 3)
|
||||
l2 = l1.appendLigament("l2", 4, 3)
|
||||
assert l2 is not None
|
||||
|
||||
# TODO... check that they do something?
|
||||
Reference in New Issue
Block a user