Skip to content

Commit 70c40a6

Browse files
committed
Add testing for examples
Fix getting-started, can-tank-drive, and can-arcade-drive for 2025 revlib Add header check and fix licenses
1 parent 384ca50 commit 70c40a6

File tree

19 files changed

+337
-90
lines changed

19 files changed

+337
-90
lines changed

.github/workflows/test.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
name: test
3+
4+
on:
5+
pull_request:
6+
push:
7+
branches:
8+
- main
9+
tags:
10+
- '*'
11+
12+
jobs:
13+
check:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: psf/black@stable
18+
19+
check-file-headers:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
- uses: actions/setup-python@v4
24+
with:
25+
python-version: 3.12
26+
- name: Check header
27+
run: python examples/check_header.py
28+
shell: bash
29+
30+
test:
31+
runs-on: ${{ matrix.os }}
32+
strategy:
33+
matrix:
34+
os: ["ubuntu-22.04", "macos-13", "windows-2022"]
35+
python_version:
36+
- '3.9'
37+
- '3.10'
38+
- '3.11'
39+
- '3.12'
40+
- '3.13'
41+
42+
steps:
43+
- uses: actions/checkout@v4
44+
- uses: actions/setup-python@v5
45+
with:
46+
python-version: ${{ matrix.python_version }}
47+
- name: Install deps
48+
run: |
49+
pip install 'robotpy[rev, commands2]<2026.0.0,>=2025.0.0b3' numpy pytest
50+
- name: Run tests
51+
run: bash examples/run_tests.sh
52+
shell: bash

examples/can-arcade-drive/robot.py

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# ----------------------------------------------------------------------------
2-
# Copyright (c) 2017-2018 FIRST. All Rights Reserved.
3-
# Open Source Software - may be modified and shared by FRC teams. The code
4-
# must be accompanied by the FIRST BSD license file in the root directory of
5-
# the project.
6-
# ----------------------------------------------------------------------------
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
77

88
import rev
99
import wpilib
@@ -25,34 +25,60 @@ def robotInit(self):
2525
#
2626
# The example below initializes four brushless motors with CAN IDs
2727
# 1, 2, 3, 4. Change these parameters to match your setup
28-
self.leftLeadMotor = rev.CANSparkMax(1, rev.CANSparkMax.MotorType.kBrushless)
29-
self.rightLeadMotor = rev.CANSparkMax(3, rev.CANSparkMax.MotorType.kBrushless)
30-
self.leftFollowMotor = rev.CANSparkMax(2, rev.CANSparkMax.MotorType.kBrushless)
31-
self.rightFollowMotor = rev.CANSparkMax(4, rev.CANSparkMax.MotorType.kBrushless)
28+
self.leftLeadMotor = rev.SparkMax(1, rev.SparkMax.MotorType.kBrushless)
29+
self.rightLeadMotor = rev.SparkMax(3, rev.SparkMax.MotorType.kBrushless)
30+
self.leftFollowMotor = rev.SparkMax(2, rev.SparkMax.MotorType.kBrushless)
31+
self.rightFollowMotor = rev.SparkMax(4, rev.SparkMax.MotorType.kBrushless)
3232

3333
# Passing in the lead motors into DifferentialDrive allows any
3434
# commmands sent to the lead motors to be sent to the follower motors.
3535
self.driveTrain = DifferentialDrive(self.leftLeadMotor, self.rightLeadMotor)
3636
self.joystick = wpilib.Joystick(0)
3737

38-
# The RestoreFactoryDefaults method can be used to reset the
39-
# configuration parameters in the SPARK MAX to their factory default
40-
# state. If no argument is passed, these parameters will not persist
41-
# between power cycles
42-
self.leftLeadMotor.restoreFactoryDefaults()
43-
self.rightLeadMotor.restoreFactoryDefaults()
44-
self.leftFollowMotor.restoreFactoryDefaults()
45-
self.rightFollowMotor.restoreFactoryDefaults()
46-
47-
# In CAN mode, one SPARK MAX can be configured to follow another. This
48-
# is done by calling the follow() method on the SPARK MAX you want to
49-
# configure as a follower, and by passing as a parameter the SPARK MAX
50-
# you want to configure as a leader.
38+
# Create new SPARK MAX configuration objects. These will store the
39+
# configuration parameters for the SPARK MAXes that we will set below.
40+
self.globalConfig = rev.SparkMaxConfig()
41+
self.rightLeaderConfig = rev.SparkMaxConfig()
42+
self.leftFollowerConfig = rev.SparkMaxConfig()
43+
self.rightFollowerConfig = rev.SparkMaxConfig()
44+
45+
# Apply the global config and invert since it is on the opposite side
46+
self.rightLeaderConfig.apply(self.globalConfig).inverted(True)
47+
48+
# Apply the global config and set the leader SPARK for follower mode
49+
self.leftFollowerConfig.apply(self.globalConfig).follow(self.leftLeadMotor)
50+
51+
# Apply the global config and set the leader SPARK for follower mode
52+
self.rightFollowerConfig.apply(self.globalConfig).follow(self.rightLeadMotor)
53+
54+
# Apply the configuration to the SPARKs.
55+
#
56+
# kResetSafeParameters is used to get the SPARK MAX to a known state. This
57+
# is useful in case the SPARK MAX is replaced.
5158
#
52-
# This is shown in the example below, where one motor on each side of
53-
# our drive train is configured to follow a lead motor.
54-
self.leftFollowMotor.follow(self.leftLeadMotor)
55-
self.rightFollowMotor.follow(self.rightLeadMotor)
59+
# kPersistParameters is used to ensure the configuration is not lost when
60+
# the SPARK MAX loses power. This is useful for power cycles that may occur
61+
# mid-operation.
62+
self.leftLeadMotor.configure(
63+
self.globalConfig,
64+
rev._rev.SparkBase.ResetMode.kResetSafeParameters,
65+
rev._rev.SparkBase.PersistMode.kPersistParameters,
66+
)
67+
self.leftFollowMotor.configure(
68+
self.leftFollowerConfig,
69+
rev._rev.SparkBase.ResetMode.kResetSafeParameters,
70+
rev._rev.SparkBase.PersistMode.kPersistParameters,
71+
)
72+
self.rightLeadMotor.configure(
73+
self.rightLeaderConfig,
74+
rev._rev.SparkBase.ResetMode.kResetSafeParameters,
75+
rev._rev.SparkBase.PersistMode.kPersistParameters,
76+
)
77+
self.rightFollowMotor.configure(
78+
self.rightFollowerConfig,
79+
rev._rev.SparkBase.ResetMode.kResetSafeParameters,
80+
rev._rev.SparkBase.PersistMode.kPersistParameters,
81+
)
5682

5783
def teleopPeriodic(self):
5884
# Drive with arcade style

examples/can-tank-drive/robot.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# ----------------------------------------------------------------------------
2-
# Copyright (c) 2017-2018 FIRST. All Rights Reserved.
3-
# Open Source Software - may be modified and shared by FRC teams. The code
4-
# must be accompanied by the FIRST BSD license file in the root directory of
5-
# the project.
6-
# ----------------------------------------------------------------------------
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
77

88
import rev
99
import wpilib
@@ -25,15 +25,22 @@ def robotInit(self):
2525
#
2626
# The example below initializes two brushless motors with CAN IDs
2727
# 1 and 2. Change these parameters to match your setup
28-
self.leftMotor = rev.CANSparkMax(1, rev.CANSparkMax.MotorType.kBrushless)
29-
self.rightMotor = rev.CANSparkMax(2, rev.CANSparkMax.MotorType.kBrushless)
30-
31-
# The RestoreFactoryDefaults method can be used to reset the
32-
# configuration parameters in the SPARK MAX to their factory default
33-
# state. If no argument is passed, these parameters will not persist
34-
# between power cycles
35-
self.leftMotor.restoreFactoryDefaults()
36-
self.rightMotor.restoreFactoryDefaults()
28+
self.leftMotor = rev.SparkMax(1, rev.SparkMax.MotorType.kBrushless)
29+
self.rightMotor = rev.SparkMax(2, rev.SparkMax.MotorType.kBrushless)
30+
31+
# Configure for factory defaults and invert right side motor
32+
self.globalConfig = rev.SparkMaxConfig()
33+
self.rightConfig = self.globalConfig.inverted(True)
34+
self.leftMotor.configure(
35+
self.globalConfig,
36+
rev._rev.SparkBase.ResetMode.kResetSafeParameters,
37+
rev._rev.SparkBase.PersistMode.kPersistParameters,
38+
)
39+
self.rightMotor.configure(
40+
self.rightConfig,
41+
rev._rev.SparkBase.ResetMode.kResetSafeParameters,
42+
rev._rev.SparkBase.PersistMode.kPersistParameters,
43+
)
3744

3845
self.driveTrain = DifferentialDrive(self.leftMotor, self.rightMotor)
3946
self.l_stick = wpilib.Joystick(0)

examples/check_header.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
7+
8+
from pathlib import Path
9+
10+
11+
def check_file_content(file_path):
12+
with open(file_path, "r") as file:
13+
lines = file.readlines()
14+
15+
if file.name.endswith("robot.py"):
16+
expected_lines = [
17+
"#!/usr/bin/env python3\n",
18+
"#\n",
19+
"# Copyright (c) FIRST and other WPILib contributors.\n",
20+
"# Open Source Software; you can modify and/or share it under the terms of\n",
21+
"# the WPILib BSD license file in the root directory of this project.\n",
22+
"#\n",
23+
"\n",
24+
]
25+
else:
26+
expected_lines = [
27+
"#\n",
28+
"# Copyright (c) FIRST and other WPILib contributors.\n",
29+
"# Open Source Software; you can modify and/or share it under the terms of\n",
30+
"# the WPILib BSD license file in the root directory of this project.\n",
31+
"#\n",
32+
"\n",
33+
]
34+
35+
if lines[: len(expected_lines)] != expected_lines:
36+
print(
37+
"\n".join(
38+
[
39+
f"{file_path}",
40+
"ERROR: File must start with the following lines",
41+
"------------------------------",
42+
"".join(expected_lines[:-1]),
43+
"------------------------------",
44+
"Found:",
45+
"".join(lines[: len(expected_lines)]),
46+
"------------------------------",
47+
]
48+
)
49+
)
50+
51+
return False
52+
return True
53+
54+
55+
def main():
56+
current_directory = Path(__file__).parent
57+
python_files = [
58+
x
59+
for x in current_directory.glob("./**/*.py")
60+
if not x.parent == current_directory and x.stat().st_size != 0
61+
]
62+
63+
non_compliant_files = [
64+
file for file in python_files if not check_file_content(file)
65+
]
66+
67+
non_compliant_files.sort()
68+
69+
if non_compliant_files:
70+
print("Non-compliant files:")
71+
for file in non_compliant_files:
72+
print(f"- {file}")
73+
exit(1) # Exit with an error code
74+
else:
75+
print("All files are compliant.")
76+
77+
78+
if __name__ == "__main__":
79+
main()

examples/color_match/robot.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/usr/bin/env python3
2-
3-
# Copyright (c) 2017-2018 FIRST. All Rights Reserved.
4-
# Open Source Software - may be modified and shared by FRC teams. The code
5-
# must be accompanied by the FIRST BSD license file in the root directory of
6-
# the project.
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
77

88
import wpilib
99
from rev import ColorSensorV3, ColorMatch

examples/get-set-params/robot.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# ----------------------------------------------------------------------------
2-
# Copyright (c) 2017-2018 FIRST. All Rights Reserved.
3-
# Open Source Software - may be modified and shared by FRC teams. The code
4-
# must be accompanied by the FIRST BSD license file in the root directory of
5-
# the project.
6-
# ----------------------------------------------------------------------------
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
77

88
import rev
99
import wpilib

examples/getting-started/robot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ def robotInit(self):
1616
This function is called upon program startup and
1717
should be used for any initialization code.
1818
"""
19-
self.leftDrive = rev.CANSparkMax(1, rev.CANSparkMax.MotorType.kBrushless)
20-
self.rightDrive = rev.CANSparkMax(2, rev.CANSparkMax.MotorType.kBrushless)
19+
self.leftDrive = rev.SparkMax(1, rev.SparkMax.MotorType.kBrushless)
20+
self.rightDrive = rev.SparkMax(2, rev.SparkMax.MotorType.kBrushless)
2121
self.robotDrive = wpilib.drive.DifferentialDrive(
2222
self.leftDrive, self.rightDrive
2323
)

examples/limit-switch/robot.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# ----------------------------------------------------------------------------
2-
# Copyright (c) 2017-2018 FIRST. All Rights Reserved.
3-
# Open Source Software - may be modified and shared by FRC teams. The code
4-
# must be accompanied by the FIRST BSD license file in the root directory of
5-
# the project.
6-
# ----------------------------------------------------------------------------
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
77

88
import rev
99
import wpilib

examples/maxswerve/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
#
12
# Copyright (c) FIRST and other WPILib contributors.
23
# Open Source Software; you can modify and/or share it under the terms of
34
# the WPILib BSD license file in the root directory of this project.
5+
#
46

57
"""
68
The constants module is a convenience place for teams to hold robot-wide

examples/maxswerve/robotcontainer.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#
2+
# Copyright (c) FIRST and other WPILib contributors.
3+
# Open Source Software; you can modify and/or share it under the terms of
4+
# the WPILib BSD license file in the root directory of this project.
5+
#
6+
17
import math
28

39
import commands2

examples/maxswerve/subsystems/drivesubsystem.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#
2+
# Copyright (c) FIRST and other WPILib contributors.
3+
# Open Source Software; you can modify and/or share it under the terms of
4+
# the WPILib BSD license file in the root directory of this project.
5+
#
6+
17
import math
28
import typing
39

examples/maxswerve/subsystems/maxswervemodule.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#
2+
# Copyright (c) FIRST and other WPILib contributors.
3+
# Open Source Software; you can modify and/or share it under the terms of
4+
# the WPILib BSD license file in the root directory of this project.
5+
#
6+
17
from rev import CANSparkMax, SparkMaxAbsoluteEncoder
28
from wpimath.geometry import Rotation2d
39
from wpimath.kinematics import SwerveModuleState, SwerveModulePosition

examples/maxswerve/swerveutils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#
2+
# Copyright (c) FIRST and other WPILib contributors.
3+
# Open Source Software; you can modify and/or share it under the terms of
4+
# the WPILib BSD license file in the root directory of this project.
5+
#
6+
17
import math
28

39

0 commit comments

Comments
 (0)