Squashed 'third_party/allwpilib/' changes from 66b57f032..e473a00f9

e473a00f9 [wpiutil] Base64: Add unsigned span/vector variants (#3702)
52f2d580e [wpiutil] raw_uv_ostream: Add reset() (#3701)
d7b1e3576 [wpiutil] WebSocket: move std::function (#3700)
93799fbe9 [examples] Fix description of TrapezoidProfileSubsystem (#3699)
b84644740 [wpimath] Document pose estimator states, inputs, and outputs (#3698)
2dc35c139 [wpimath] Fix classpaths for JNI class loads (#3697)
2cb171f6f [docs] Set Doxygen extract_all to true and fix Doxygen failures (#3695)
a939cd9c8 [wpimath] Print uncontrollable/unobservable models in LQR and KF (#3694)
d5270d113 [wpimath] Clean up C++ StateSpaceUtil tests (#3692)
b20903960 [wpimath] Remove redundant discretization tests from StateSpaceUtilTest (#3689)
c0cb545b4 [wpilibc] Add deprecated Doxygen attribute to SpeedController (#3691)
35c9f66a7 [wpilib] Rename PneumaticsHub to PneumaticHub (#3686)
796d03d10 [wpiutil] Remove unused LLVM header (#3688)
8723caf78 [wpilibj] Make Java TrapezoidProfile.Constraints an immutable class (#3687)
187f50a34 [wpimath] Catch incorrect parameters to state-space models earlier (#3680)
8d04606c4 Replace instances of frc-characterization with SysId (NFC) (#3681)
b82d4f6e5 [hal, cscore, ntcore] Use WPI common handle type base
87e34967e [wpiutil] Add synchronization primitives
e32499c54 [wpiutil] Add ParallelTcpConnector (#3655)
aa0b49228 [wpilib] Remove redundant "quick turn" docs for curvature drive (NFC) (#3674)
57301a7f9 [hal] REVPH: Start closed-loop compressor control on init (#3673)
d1842ea8f [wpilib] Improve interrupt docs (NFC) (#3679)
558151061 [wpiutil] Add DsClient (#3654)
181723e57 Replace `.to<double>()` and `.template to<double>()` with `.value()` (#3667)
6bc1db44b [commands] Add pointer overload of AddRequirements (artf6003) (#3669)
737b57ed5 [wpimath] Update to drake v0.35.0 (#3665)
4d287d1ae [build] Upgrade WPIJREArtifact to JRE 2022-11.0.12u5 (#3666)
f26eb5ada [hal] Fix another typo (get -> gets) (NFC) (#3663)
94ed275ba [hal] Fix misspelling (numer -> number) (NFC) (#3662)
ac2f44da3 [wpiutil] uv: use move for std::function (#3653)
75fa1fbfb [wpiutil] json::serializer: Optimize construction (#3647)
5e689faea [wpiutil] Import MessagePack implementation (mpack) (#3650)
649a50b40 [wpiutil] Add LEB128 byte-by-byte reader (#3652)
e94397a97 [wpiutil] Move json_serializer.h to public headers (#3646)
4ec58724d [wpiutil] uv::Tcp: Clarify SetNoDelay documentation (#3649)
8cb294aa4 [wpiutil] WebSocket: Make Shutdown() public (#3651)
2b3a9a52b [wpiutil] json: Fix map iterator key() for std::string_view (#3645)
138cbb94b [wpiutil] uv::Async: Add direct call for no-parameter specialization (#3648)
e56d6dea8 [ci] Update testbench pool image to ubuntu-latest (#3643)
43f30e44e [build] Enable comments in doxygen source files (#3644)
9e6db17ef [build] Enable doxygen preprocessor expansion of WPI_DEPRECATED (#3642)
0e631ad2f Add WPILib version to issue template (#3641)
6229d8d2f [build] Docs: set case_sense_names to false (#3392)
4647d09b5 [docs] Fix Doxygen warnings, add CI docs lint job (#3639)
4ad3a5402 [hal] Fix PWM allocation channel (#3637)
05e5feac4 [docs] Fix brownout docs (NFC) (#3638)
67df469c5 [examples] Remove old command-based templates and examples (#3263)
689e9ccfb [hal, wpilib] Add brownout voltage configuration (#3632)
9cd4bc407 [docs] Add useLocal to avoid using installer artifacts (#3634)
61996c2bb [cscore] Fix Java direct callback notifications (#3631)
6d3dd99eb [build] Update to newest native-utils (#3633)
f0b484892 [wpiutil] Fix StringMap iterator equality check (#3629)
8352cbb7a Update development build instructions for 2022 (#3616)
6da08b71d [examples] Fix Intermediate Vision Java Example description (#3628)
5d99059bf [wpiutil] Remove optional.h (#3627)
fa41b106a [glass, wpiutil] Add missing format args (#3626)
4e3fd7d42 [build] Enable Zc:__cplusplus for Windows (#3625)
791d8354d [build] Suppress deprecation/removal warnings for old commands (#3618)
10f19e6fc [hal, wpilib] Add REV PneumaticsHub (#3600)
4c61a1305 [ntcore] Revert to per-element copy for toNative() (#3621)
7b3f62244 [wpiutil] SendableRegistry: Print exception stacktrace (#3620)
d347928e4 [hal] Use better error for when console out is enabled while attempting to use onboard serial port (#3622)
cc31079a1 [hal] Use setcap instead of setuid for setting thread priorities (#3613)
4676648b7 [wpimath] Upgrade to Drake v0.34.0 (#3607)
c7594c911 [build] Allow building wpilibc in cmake without cscore and opencv (#3605)
173cb7359 [wpilib] Add TimesliceRobot (#3502)
af295879f [hal] Set error status for I2C port out of range (#3603)
95dd20a15 [build] Enable spotbugs (#3601)
b65fce86b [wpilib] Remove Timer lock in wpilibj and update docs (#3602)
3b8d3bbcb Remove unused and add missing deprecated.h includes (#3599)
f9e976467 [examples] Rename DriveTrain classes to Drivetrain (#3594)
118a27be2 [wpilib] Add Timer tests (#3595)
59c89428e [wpilib] Deprecate Timer::HasPeriodPassed() (#3598)
202ca5e78 Force C++17 in .clang-format (#3597)
d6f185d8e Rename tests for consistency (#3592)
54ca474db [ci] Enable asan and tsan in CI for tests that pass (#3591)
1ca383b23 Add Debouncer (#3590)
179fde3a7 [build] Update to 2022 native utils and gradle 7 (#3588)
50198ffcf [examples] Add Mechanism2d visualization to Elevator Sim (#3587)
a446c2559 [examples] Synchronize C++ and Java Mechanism2d examples (#3589)
a7fb83103 [ci] clang-tidy: Generate compilation commands DB with Gradle (#3585)
4f5e0c9f8 [examples] Update ArmSimulation example to use Mechanism2d (#3572)
8164b91dc [CI] Print CMake test output on failure (#3583)
4d5fca27e [wpilib] Impove Mechanism2D documentation (NFC) (#3584)
fe59e4b9f Make C++ test names more consistent (#3586)
5c8868549 [wpilibc] Fix C++ MechanisimRoot2D to use same NT entries as Java/Glass (#3582)
9359431ba [wpimath] Clean up Eigen usage
72716f51c [wpimath] Upgrade to Eigen 3.4
382deef75 [wpimath] Explicitly export wpimath symbols
161e21173 [ntcore] Match standard handle layout, only allow 16 instances (#3577)
263a24811 [wpimath] Use jinja for codegen (#3574)
725251d29 [wpilib] Increase tolerances of DifferentialDriveSimTest (#3581)
4dff87301 [wpimath] Make LinearFilter::Factorial() constexpr (#3579)
60ede67ab [hal, wpilib] Switch PCM to be a single object that is allowed to be duplicated (#3475)
906bfc846 [build] Add CMake build support for sanitizers (#3576)
0d4f08ad9 [hal] Simplify string copy of joystick name (#3575)
a52bf87b7 [wpiutil] Add Java function package (#3570)
40c7645d6 [wpiutil] UidVector: Return old object from erase() (#3571)
5b886a23f [wpiutil] jni_util: Add size, operator[] to JArrayRef (#3569)
65797caa7 [sim] Fix halsim_ds_socket stringop overflow warning from GCC 10 (#3568)
66abb3988 [hal] Update runtime enum to allow selecting roborio 2 (#3565)
95a12e0ee [hal] UidSetter: Don't revert euid if its already current value (#3566)
27951442b [wpimath] Use external Eigen headers only (#3564)
c42e053ae [docs] Update to doxygen 1.9.2 (#3562)
e7048c8c8 [docs] Disable doxygen linking for common words that are also classes (#3563)
d8e0b6c97 [wpilibj] Fix java async interrupts (#3559)
5e6c34c61 Update to 2022 roborio image (#3537)
828f073eb [wpiutil] Fix uv::Buffer memory leaks caught by asan (#3555)
2dd5701ac [cscore] Fix mutex use-after-free in cscore test (#3557)
531439198 [ntcore] Fix NetworkTables memory leaks caught by asan (#3556)
3d9a4d585 [wpilibc] Fix AnalogTriggerOutput memory leak reported by asan (#3554)
54eda5928 [wpiutil] Ignore ubsan vptr upcast warning in SendableHelper moves (#3553)
5a4f75c9f [wpilib] Replace Speed controller comments with motor controller (NFC) (#3551)
7810f665f [wpiutil] Fix bug in uleb128 (#3540)
697e2dd33 [wpilib] Fix errant jaguar reference in comments (NFC) (#3550)
936c64ff5 [docs] Enable -linksource for javadocs (#3549)
1ea654954 [build] Upgrade CMake build to googletest 1.11.0 (#3548)
32d9949e4 [wpimath] Move controller tests to wpimath (#3541)
01ba56a8a [hal] Replace strncpy with memcpy (#3539)
e109c4251 [build] Rename makeSim flag to forceGazebo to better describe what it does (#3535)
e4c709164 [docs] Use a doxygen theme and add logo (#3533)
960b6e589 [wpimath] Fix Javadoc warning (#3532)
82eef8d5e [hal] Remove over current fault HAL functions from REV PDH (#3526)
aa3848b2c [wpimath] Move RobotDriveBase::ApplyDeadband() to MathUtil (#3529)
3b5d0d141 [wpimath] Add LinearFilter::BackwardFiniteDifference() (#3528)
c8fc715fe [wpimath] Upgrade drake files to v0.33.0 (#3531)
e5fe3a8e1 [build] Treat javadoc warnings as errors in CI and fix warnings (#3530)
e0c6cd3dc [wpimath] Add an operator for composing two Transform2ds (#3527)
2edd510ab [sim] Add sim wrappers for sensors that use SimDevice (#3517)
2b3e2ebc1 [hal] Fix HAL Notifier thread priority setting (#3522)
ab4cb5932 [gitignore] Update gitignore to ignore bazel / clion files (#3524)
57c8615af [build] Generate spotless patch on failure (#3523)
b90317321 Replace std::cout and std::cerr with fmt::print() (#3519)
10cc8b89c [hal] [wpilib] Add initial support for the REV PDH (#3503)
5d9ae3cdb [hal] Set HAL Notifier thread as RT by default (#3482)
192d251ee [wpilibcIntegrationTests] Properly disable DMA integration tests (#3514)
031962608 [wpilib] Add PS4Controller, remove Hand from GenericHID/XboxController (#3345)
25f6f478a [wpilib] Rename DriverStation::IsOperatorControl() to IsTeleop() (#3505)
e80f09f84 [wpilibj] Add unit tests (#3501)
c159f91f0 [wpilib] Only read DS control word once in IterativeRobotBase (#3504)
eb790a74d Add rio development docs documenting myRobot deploy tasks (#3508)
e47451f5a [wpimath] Replace auto with Eigen types (#3511)
252b8c83b Remove Java formatting from build task in CI (#3507)
09666ff29 Shorten Gazebo CI build (#3506)
baf2e501d Update myRobot to use 2021 java (#3509)
5ac60f0a2 [wpilib] Remove IterativeRobotBase mode init prints (#3500)
fb2ee8ec3 [wpilib] Add TimedRobot functions for running code on mode exit (#3499)
94e0db796 [wpilibc] Add more unit tests (#3494)
b25324695 [wpilibj] Add units to parameter names (NFC) (#3497)
1ac73a247 [hal] Rename PowerDistributionPanel to PowerDistribution (#3466)
2014115bc [examples] frisbeebot: Fix typo and reflow comments (NFC) (#3498)
4a944dc39 [examples] Consistently use 0 for controller port (#3496)
3838cc4ec Use unicode characters in docs equations (#3487)
85748f2e6 [examples] Add C++ TankDrive example (#3493)
d7b8aa56d [wpilibj] Rename DriverStation In[Mode] functions to follow style guide (#3488)
16e096cf8 [build] Fix CMake Windows CI (#3490)
50af74c38 [wpimath] Clean up NumericalIntegration and add Discretization tests (#3489)
bfc209b12 Automate fmt update (#3486)
e7f9331e4 [build] Update to Doxygen 1.9.1 (#3008)
ab8e8aa2a [wpimath] Update drake with upstream (#3484)
1ef826d1d [wpimath] Fix IOException path in WPIMath JNI (#3485)
52bddaa97 [wpimath] Disable iostream support for units and enable fmtlib (#3481)
e4dc3908b [wpiutil] Upgrade to fmtlib 8.0.1 (#3483)
1daadb812 [wpimath] Implement Dormand-Prince integration method (#3476)
9c2723391 [cscore] Add [[nodiscard]] to GrabFrame functions (#3479)
7a8796414 [wpilib] Add Notifier integration tests (#3480)
f8f13c536 [wpilibcExamples] Prefix decimal numbers with 0 (#3478)
1adb69c0f [ntcore] Use "NetworkTables" instead of "Network Tables" in NT specs (#3477)
5f5830b96 Upload wpiformat diff if one exists (#3474)
9fb4f35bb [wpimath] Add tests for DARE overload with Q, R, and N matrices (#3472)
c002e6f92 Run wpiformat (#3473)
c154e5262 [wpilib] Make solenoids exclusive use, PCM act like old sendable compressor (#3464)
6ddef1cca [hal] JNI setDIO: use a boolean and not a short (#3469)
9d68d9582 Remove extra newlines after open curly braces (NFC) (#3471)
a4233e1a1 [wpimath] Add script for updating Drake (#3470)
39373c6d2 Update README.md for new GCC version requirement (#3467)
d29acc90a [wpigui] Add option to reset UI on exit (#3463)
a371235b0 [ntcore] Fix dangling pointer in logger (#3465)
53b4891a5 [wpilibcintegrationtests] Fix deprecated Preferences usage (#3461)
646ded912 [wpimath] Remove incorrect discretization in pose estimators (#3460)
ea0b8f48e Fix some deprecation warnings due to fmtlib upgrade (#3459)
2067d7e30 [wpilibjexamples] Add wpimathjni, wpiutiljni to library path (#3455)
866571ab4 [wpiutil] Upgrade to fmtlib 8.0.0 (#3457)
4e1fa0308 [build] Skip PDB copy on windows build servers (#3458)
b45572167 [build] Change CI back to 18.04 docker images (#3456)
57a160f1b [wpilibc] Fix LiveWindow deprecation warning in RobotBase skeleton template (#3454)
29ae8640d [HLT] Implement duty cycle cross connect tests (#3453)
ee6377e54 [HLT] Add relay and analog cross connects (#3452)
b0f1ae7ea [build] CMake: Build the HAL even if WITH_CSCORE=OFF (#3449)
7aae2b72d Replace std::to_string() with fmt::format() (#3451)
73fcbbd74 [HLT] Add relay digital cross connect tests (#3450)
e7bedde83 [HLT] Add PWM tests that use DMA as the back end (#3447)
7253edb1e [wpilibc] Timer: Fix deprecated warning (#3446)
efa28125c [wpilibc] Add message to RobotBase on how to read stacktrace (#3444)
9832fcfe1 [hal] Fix DIO direction getter (#3445)
49c71f9f2 [wpilibj] Clarify robot quit message (#3364)
791770cf6 [wpimath] Move controller from wpilibj to wpimath (#3439)
9ce9188ff [wpimath] Add ReportWarning to MathShared (#3441)
362066a9b [wpilib] Deprecate getInstance() in favor of static functions (#3440)
26ff9371d Initial commit of cross connect integration test project (#3434)
4a36f86c8 [hal] Add support for DMA to Java (#3158)
85144e47f [commands] Unbreak build (#3438)
b417d961e Split Sendable into NT and non-NT portions (#3432)
ef4ea84cb [commands] Change grouping decorator impl to flatten nested group structures (#3335)
b422665a3 [examples] Invert right side of drive subsystems (#3437)
186dadf14 [hal] Error if attempting to set DIO output on an input port (#3436)
04e64db94 Remove redundant C++ lambda parentheses (NFC) (#3433)
f60994ad2 [wpiutil] Rename Java package to edu.wpi.first.util (#3431)
cfa1ca96f [wpilibc] Make ShuffleboardValue non-copyable (#3430)
4d9ff7643 Fix documentation warnings generated by JavaDoc (NFC) (#3428)
9e1b7e046 [build] Fix clang-tidy and clang-format (#3429)
a77c6ff3a [build] Upgrade clang-format and clang-tidy (NFC) (#3422)
099fde97d [wpilib] Improve PDP comments (NFC) (#3427)
f8fc2463e [wpilibc, wpiutil] Clean up includes (NFC) (#3426)
e246b7884 [wpimath] Clean up member initialization in feedforward classes (#3425)
c1e128bd5 Disable frivolous PMD warnings and enable PMD in ntcore (#3419)
8284075ee Run "Lint and Format" CI job on push as well as pull request (#3412)
f7db09a12 [wpimath] Move C++ filters into filter folder to match Java (#3417)
f9c3d54bd [wpimath] Reset error covariance in pose estimator ResetPosition() (#3418)
0773f4033 [hal] Ensure HAL status variables are initialized to zero (#3421)
d068fb321 [build] Upgrade CI to use 20.04 docker images (#3420)
8d054c940 [wpiutil] Remove STLExtras.h
80f1d7921 [wpiutil] Split function_ref to a separate header
64f541325 Use wpi::span instead of wpi::ArrayRef across all libraries (#3414)
2abbbd9e7 [build] clang-tidy: Remove bugprone-exception-escape (#3415)
a5c471af7 [wpimath] Add LQR template specialization for 2x2 system
edd2f0232 [wpimath] Add DARE solver for Q, R, and N with LQR ctor overloads
b2c3b2dd8 Use std::string_view and fmtlib across all libraries (#3402)
4f1cecb8e [wpiutil] Remove Path.h (#3413)
b336eac34 [build] Publish halsim_ws_core to Maven
2a09f6fa4 [build] Also build sim modules as static libraries
0e702eb79 [hal] Add a unified PCM object (#3331)
dea841103 [wpimath] Add fmtlib formatter overloads for Eigen::Matrix and units (#3409)
82856cf81 [wpiutil] Improve wpi::circular_buffer iterators (#3410)
8aecda03e [wpilib] Fix a documentation typo (#3408)
5c817082a [wpilib] Remove InterruptableSensorBase and replace with interrupt classes (#2410)
15c521a7f [wpimath] Fix drivetrain system identification (#3406)
989de4a1b [build] Force all linker warnings to be fatal for rio builds (#3407)
d9eeb45b0 [wpilibc] Add units to Ultrasonic class API (#3403)
fe570e000 [wpiutil] Replace llvm filesystem with C++17 filesystem (#3401)
01dc0249d [wpimath] Move SlewRateLimiter from wpilib to wpimath (#3399)
93523d572 [wpilibc] Clean up integration tests (#3400)
4f7a4464d [wpiutil] Rewrite StringExtras for std::string_view (#3394)
e09293a15 [wpilibc] Transition C++ classes to units::second_t (#3396)
827b17a52 [build] Create run tasks for Glass and OutlineViewer (#3397)
a61037996 [wpiutil] Avoid MSVC warning on span include (#3393)
4e2c3051b [wpilibc] Use std::string_view instead of Twine (#3380)
50915cb7e [wpilibc] MotorSafety::GetDescription(): Return std::string (#3390)
f4e2d26d5 [wpilibc] Move NullDeleter from frc/Base.h to wpi/NullDeleter.h (#3387)
cb0051ae6 [wpilibc] SimDeviceSim: use fmtlib (#3389)
a238cec12 [wpiutil] Deprecate wpi::math constants in favor of wpi::numbers (#3383)
393bf23c0 [ntcore, cscore, wpiutil] Standardize template impl files on .inc extension (NFC) (#3124)
e7d9ba135 [sim] Disable flaky web server integration tests (#3388)
0a0003c11 [wpilibjExamples] Fix name of Java swerve drive pose estimator example (#3382)
7e1b27554 [wpilibc] Use default copies and moves when possible (#3381)
fb2a56e2d [wpilibc] Remove START_ROBOT_CLASS macro (#3384)
84218bfb4 [wpilibc] Remove frc namespace shim (#3385)
dd7824340 [wpilibc] Remove C++ compiler version static asserts (#3386)
484cf9c0e [wpimath] Suppress the -Wmaybe-uninitialized warning in Eigen (#3378)
a04d1b4f9 [wpilibc] DriverStation: Remove ReportError and ReportWarning
831c10bdf [wpilibc] Errors: Use fmtlib
87603e400 [wpiutil] Import fmtlib (#3375)
442621672 [wpiutil] Add ArrayRef/std::span/wpi::span implicit conversions
bc15b953b [wpiutil] Add std::span implementation
6d20b1204 [wpiutil] StringRef, Twine, raw_ostream: Add std::string_view support (#3373)
2385c2a43 [wpilibc] Remove Utility.h (#3376)
87384ea68 [wpilib] Fix PIDController continuous range error calculations (#3170)
04dae799a [wpimath] Add SimpleMotorFeedforward::Calculate(velocity, nextVelocity) overload (#3183)
0768c3903 [wpilib] DifferentialDrive: Remove right side inversion (#3340)
8dd8d4d2d [wpimath] Fix redundant nested math package introduced by #3316 (#3368)
49b06beed [examples] Add Field2d to RamseteController example (#3371)
4c562a445 [wpimath] Fix typo in comment of update_eigen.py (#3369)
fdbbf1188 [wpimath] Add script for updating Eigen
f1e64b349 [wpimath] Move Eigen unsupported folder into eigeninclude
224f3a05c [sim] Fix build error when building with GCC 11.1 (#3361)
ff56d6861 [wpilibj] Fix SpeedController deprecated warnings (#3360)
1873fbefb [examples] Fix Swerve and Mecanum examples (#3359)
80b479e50 [examples] Fix SwerveBot example to use unique encoder ports (#3358)
1f7c9adee [wpilibjExamples] Fix pose estimator examples (#3356)
9ebc3b058 [outlineviewer] Change default size to 600x400 (#3353)
e21b443a4 [build] Gradle: Make C++ examples runnable (#3348)
da590120c [wpilibj] Add MotorController.setVoltage default (#3347)
561d53885 [build] Update opencv to 4.5.2, imgui/implot to latest (#3344)
44ad67ca8 [wpilibj] Preferences: Add missing Deprecated annotation (#3343)
3fe8fc75a [wpilibc] Revert "Return reference from GetInstance" (#3342)
3cc2da332 Merge branch '2022'
a3cd90dd7 [wpimath] Fix classpath used by generate_numbers.py (#3339)
d6cfdd3ba [wpilib] Preferences: Deprecate Put* in favor of Set* (#3337)
ba08baabb [wpimath] Update Drake DARE solver to v0.29.0 (#3336)
497b712f6 [wpilib] Make IterativeRobotBase::m_period private with getter
f00dfed7a [wpilib] Remove IterativeRobot base class
3c0846168 [hal] Use last error reporting instead of PARAMETER_OUT_OF_RANGE (#3328)
5ef2b4fdc [wpilibj] Fix @deprecated warning for SerialPort constructor (#3329)
23d2326d1 [hal] Report previous allocation location for indexed resource duplicates (#3322)
e338f9f19 [build] Fix wpilibc runCpp task (#3327)
c8ff626fe [wpimath] Move Java classes to edu.wpi.first.math (#3316)
4e424d51f [wpilibj] DifferentialDrivetrainSim: Rename constants to match the style guide (#3312)
6b50323b0 [cscore] Use Lock2DSize if possible for Windows USB cameras (#3326)
65c148536 [wpilibc] Fix "control reaches end of non-void function" warning (#3324)
f99f62bee [wpiutil] uv Handle: Use malloc/free instead of new/delete (#3325)
365f5449c [wpimath] Fix MecanumDriveKinematics (#3266)
ff52f207c [glass, wpilib] Rewrite Mechanism2d (#3281)
ee0eed143 [wpimath] Add DCMotor factory function for Romi motors (#3319)
512738072 [hal] Add HAL_GetLastError to enable better error messages from HAL calls (#3320)
ced654880 [glass, outlineviewer] Update Mac icons to macOS 11 style (#3313)
936d3b9f8 [templates] Add Java template for educational robot (#3309)
6e31230ad [examples] Fix odometry update in SwerveControllerCommand example (#3310)
05ebe9318 Merge branch 'main' into 2022
aaf24e255 [wpilib] Fix initial heading behavior in HolonomicDriveController (#3290)
8d961dfd2 [wpilibc] Remove ErrorBase (#3306)
659b37ef9 [wpiutil] StackTrace: Include offset on Linux (#3305)
0abf6c904 [wpilib] Move motor controllers to motorcontrol package (#3302)
4630191fa [wpiutil] circular_buffer: Use value initialization instead of passing zero (#3303)
b7b178f49 [wpilib] Remove Potentiometer interface
687066af3 [wpilib] Remove GyroBase
6b168ab0c [wpilib] Remove PIDController, PIDOutput, PIDSource
948625de9 [wpimath] Document conversion from filter cutoff frequency to time constant (#3299)
3848eb8b1 [wpilibc] Fix flywhel -> flywheel typo in FlywheelSim (#3298)
3abe0b9d4 [cscore] Move java package to edu.wpi.first.cscore (#3294)
d7fabe81f [wpilib] Remove RobotDrive (#3295)
1dc81669c [wpilib] Remove GearTooth (#3293)
01d0e1260 [wpilib] Revert move of RomiGyro into main wpilibc/j (#3296)
397e569aa [ntcore] Remove "using wpi" from nt namespace
79267f9e6 [ntcore] Remove NetworkTable -> nt::NetworkTable shim
48ebe5736 [ntcore] Remove deprecated Java interfaces and classes
c2064c78b [ntcore] Remove deprecated ITable interfaces
36608a283 [ntcore] Remove deprecated C++ APIs
a1c87e1e1 [glass] LogView: Add "copy to clipboard" button (#3274)
fa7240a50 [wpimath] Fix typo in quintic spline basis matrix
ffb4d38e2 [wpimath] Add derivation for spline basis matrices
f57c188f2 [wpilib] Add AnalogEncoder(int) ctor (#3273)
8471c4fb2 [wpilib] FieldObject2d: Add setTrajectory() method (#3277)
c97acd18e [glass] Field2d enhancements (#3234)
ffb590bfc [wpilib] Fix Compressor sendable properties (#3269)
6137f98eb [hal] Rename SimValueCallback2 to SimValueCallback (#3212)
a6f653969 [hal] Move registerSimPeriodic functions to HAL package (#3211)
10c038d9b [glass] Plot: Fix window creation after removal (#3264)
2d2eaa3ef [wpigui] Ensure window will be initially visible (#3256)
4d28b1f0c [wpimath] Use JNI for trajectory serialization (#3257)
3de800a60 [wpimath] TrajectoryUtil.h: Comment formatting (NFC) (#3262)
eff592377 [glass] Plot: Don't overwrite series ID (#3260)
a79faace1 [wpilibc] Return reference from GetInstance (#3247)
9550777b9 [wpilib] PWMSpeedController: Use PWM by composition (#3248)
c8521a3c3 [glass] Plot: Set reasonable default window size (#3261)
d71eb2cf3 [glass] Plot: Show full source name as tooltip and in popup (#3255)
160fb740f [hal] Use std::lround() instead of adding 0.5 and truncating (#3012)
48e9f3951 [wpilibj] Remove wpilibj package CameraServer (#3213)
8afa596fd [wpilib] Remove deprecated Sendable functions and SendableBase (#3210)
d3e45c297 [wpimath] Make C++ geometry classes immutable (#3249)
2c98939c1 [glass] StringChooser: Don't call SameLine() at end
a18a7409f [glass] NTStringChooser: Clear value of deleted entries
2f19cf452 [glass] NetworkTablesHelper: listen to delete events
da96707dc Merge branch 'main' into 2022
c3a8bdc24 [build] Fix clang-tidy action (#3246)
21624ef27 Add ImGui OutlineViewer (#3220)
1032c9b91 [wpiutil] Unbreak wpi::Format on Windows (#3242)
2e07902d7 [glass] NTField2D: Fix name lookup (#3233)
6e23e1840 [wpilibc] Remove WPILib.h (#3235)
3e22e4506 [wpilib] Make KoP drivetrain simulation weight 60 lbs (#3228)
79d1bd6c8 [glass] NetworkTablesSetting: Allow disable of server option (#3227)
fe341a16f [examples] Use more logical elevator setpoints in GearsBot (#3198)
62abf46b3 [glass] NetworkTablesSettings: Don't block GUI (#3226)
a95a5e0d9 [glass] Move NetworkTablesSettings to libglassnt (#3224)
d6f6ceaba [build] Run Spotless formatter (NFC) (#3221)
0922f8af5 [commands] CommandScheduler.requiring(): Note return can be null (NFC) (#2934)
6812302ff [examples] Make DriveDistanceOffboard example work in sim (#3199)
f3f86b8e7 [wpimath] Add pose estimator overload for vision + std dev measurement (#3200)
1a2680b9e [wpilibj] Change CommandBase.withName() to return CommandBase (#3209)
435bbb6a8 [command] RamseteCommand: Output 0 if interrupted (#3216)
3cf44e0a5 [hal] Add function for changing HAL Notifier thread priority (#3218)
40b367513 [wpimath] Units.java: Add kg-lb conversions (#3203)
9f563d584 [glass] NT: Fix return value in StringToDoubleArray (#3208)
af4adf537 [glass] Auto-size plots to fit window (#3193)
2560146da [sim] GUI: Add option to show prefix in Other Devices (#3186)
eae3a6397 gitignore: Ignore .cache directory (#3196)
959611420 [wpilib] Require non-zero positive value for PIDController.period (#3175)
9522f2e8c [wpimath] Add methods to concatenate trajectories (#3139)
e42a0b6cf [wpimath] Rotation2d comment formatting (NFC) (#3162)
d1c7032de [wpimath] Fix order of setting gyro offset in pose estimators (#3176)
d241bc81a [sim] Add DoubleSolenoidSim and SolenoidSim classes (#3177)
cb7f39afa [wpilibc] Add RobotController::GetBatteryVoltage() to C++ (#3179)
99b5ad9eb [wpilibj] Fix warnings that are not unused variables or deprecation (#3161)
c14b23775 [build] Fixup doxygen generated include dirs to match what users would need (#3154)
d447c7dc3 [sim] Add SimDeviceSim ctor overloads (#3134)
247420c9c [build] Remove jcenter repo (#3157)
04b112e00 [build] Include debug info in plugin published artifacts (#3149)
be0ce9900 [examples] Use PWMSparkMax instead of PWMVictorSPX (#3156)
69e8d0b65 [wpilib] Move RomiGyro into main wpilibc/j (#3143)
94e685e1b [wpimath] Add custom residual support to EKF (#3148)
5899f3dd2 [sim] GUI: Make keyboard settings loading more robust (#3167)
f82aa1d56 [wpilib] Fix HolonomicDriveController atReference() behavior (#3163)
fe5c2cf4b [wpimath] Remove ControllerUtil.java (#3169)
43d40c6e9 [wpiutil] Suppress unchecked cast in CombinedRuntimeLoader (#3155)
3d44d8f79 [wpimath] Fix argument order in UKF docs (NFC) (#3147)
ba6fe8ff2 [cscore] Add USB camera change event (#3123)
533725888 [build] Tweak OpenCV cmake search paths to work better on Linux (#3144)
29bf9d6ef [cscore] Add polled support to listener
483beb636 [ntcore] Move CallbackManager to wpiutil
fdaec7759 [examples] Instantiate m_ramseteController in example (#3142)
8494a5761 Rename default branch to main (#3140)
45590eea2 [wpigui] Hardcode window scale to 1 on macOS (#3135)
834a64920 [build] Publish libglass and libglassnt to Maven (#3127)
2c2ccb361 [wpimath] Fix Rotation2d equality operator (#3128)
fb5c8c39a [wpigui] clang-tidy: readability-braces-around-statements
f7d39193a [wpigui] Fix copyright in pfd and wpigui_metal.mm
aec796b21 [ntcore] Fix conditional jump on uninitialized value (#3125)
fb13bb239 [sim] GUI: Add right click popup for keyboard joystick settings (#3119)
c517ec677 [build] Update thirdparty-imgui to 1.79-2 (#3118)
e8cbf2a71 [wpimath] Fix typo in SwerveDrivePoseEstimator doc (NFC) (#3112)
e9c86df46 [wpimath] Add tests for swerve module optimization (#3100)
6ba8c289c [examples] Remove negative of ArcadeDrive(fwd, ..) in the C++ Getting Started Example (#3102)
3f1672e89 [hal] Add SimDevice createInt() and createLong() (#3110)
15be5cbf1 [examples] Fix segfault in GearsBot C++ example (#3111)
4cf0e5e6d Add quick links to API documentation in README (#3082)
6b1898f12 Fix RT priority docs (NFC) (#3098)
b3426e9c0 [wpimath] Fix missing whitespace in pose estimator doc (#3097)
38c1a1f3e [examples] Fix feildRelative -> fieldRelative typo in XControllerCommand examples (#3104)
4488e25f1 [glass] Shorten SmartDashboard window names (#3096)
cfdb3058e [wpilibj] Update SimDeviceSimTest (#3095)
64adff5fe [examples] Fix typo in ArcadeDrive constructor parameter name (#3092)
6efc58e3d [build] Fix issues with build on windows, deprecations, and native utils (#3090)
f393989a5 [wpimath, wpiutil] Add wpi::array for compile time size checking (#3087)
d6ed20c1e [build] Set macOS deployment target to 10.14 (#3088)
7c524014c [hal] Add [[nodiscard]] to HAL_WaitForNotifierAlarm() (#3085)
406d055f0 [wpilib] Fixup wouldHitLowerLimit in elevator and arm simulation classes. (#3076)
04a90b5dd [examples] Don't continually set setpoint in PotentiometerPID Examples (#3084)
8c5bfa013 [sim] GUI: Add max value setting for keyboard joysticks (#3083)
bc80c5535 [hal] Add SimValue reset() function (#3064)
9c3b51ca0 [wpilib] Document simulation APIs (#3079)
26584ff14 [wpimath] Add model description to LinearSystemId Javadocs (#3080)
42c3d5286 [examples] Sync Java and C++ trajectories in sim example (#3081)
64e72f710 [wpilibc] Add missing function RoboRioSim::ResetData (#3073)
e95503798 [wpimath] Add optimize() to SwerveModuleState (#3065)
fb99910c2 [hal] Add SimInt and SimLong wrappers for int/long SimValue (#3066)
e620bd4d3 [doc] Add machine-readable websocket specification (#3059)
a44e761d9 [glass] Add support for plot Y axis labels
ea1974d57 [wpigui] Update imgui and implot to latest
85a0bd43c [wpimath] Add RKF45 integration (#3047)
278e0f126 [glass] Use .controllable to set widgets' read-only state (#3035)
d8652cfd4 [wpimath] Make Java DCMotor API consistent with C++ and fix motor calcs (#3046)
377b7065a [build] Add toggleOffOn to Java spotless (#3053)
1e9c79c58 [sim] Use plant output to retrieve simulated position (#3043)
78147aa34 [sim] GUI: Fix Keyboard Joystick (#3052)
cd4a2265b [ntcore] Fix NetworkTableEntry::GetRaw() (#3051)
767ac1de1 [build] Use deploy key for doc publish (#3048)
d762215d1 [build] Add publish documentation script (#3040)
1fd09593c [examples] Add missing TestInit method to GettingStarted Example (#3039)
e45a0f6ce [examples] Add RomiGyro to the Romi Reference example (#3037)
94f852572 Update imaging link and fix typo (#3038)
d73cf64e5 [examples] Update RomiReference to match motor directions (#3036)
f945462ba Bump copyright year to 2021 (#3033)
b05946175 [wpimath] Catch Drake JNI exceptions and rethrow them (#3032)
62f0f8190 [wpimath] Deduplicate angle modulus functions (#2998)
bf8c0da4b [glass] Add "About" popup with version number (#3031)
dfdd6b389 [build] Increase Gradle heap size in Gazebo build (#3028)
f5e0fc3e9 Finish clang-tidy cleanups (#3003)
d741101fe [sim] Revert accidental commit of WSProvider_PDP.h (#3027)
e1620799c [examples] Add C++ RomiReference example (#2969)
749c7adb1 [command] Fix use-after-free in CommandScheduler (#3024)
921a73391 [sim] Add WS providers for AddressableLED, PCM, and Solenoid (#3026)
26d0004fe [build] Split Actions into different yml files (#3025)
948af6d5b [wpilib] PWMSpeedController.get(): Apply Inversion (#3016)
670a187a3 [wpilibc] SuppliedValueWidget.h: Forward declare ShuffleboardContainer (#3021)
be9f72502 [ntcore] NetworkTableValue: Use std::forward instead of std::move (#3022)
daf3f4cb1 [cscore] cscore_raw_cv.h: Fix error in PutFrame() (#3019)
5acda4cc7 [wpimath] ElevatorFeedforward.h: Add time.h include
8452af606 [wpimath] units/radiation.h: Add mass.h include
630d44952 [hal] ErrorsInternal.h: Add stdint.h include
7372cf7d9 [cscore] Windows NetworkUtil.cpp: Add missing include
b7e46c558 Include .h from .inc/.inl files (NFC) (#3017)
bf8f8710e [examples] Update Romi template and example (#2996)
6ffe5b775 [glass] Ensure NetworkTableTree parent context menu has an id (#3015)
be0805b85 [build] Update to WPILibVersioningPlugin 4.1.0 (#3014)
65b2359b2 [build] Add spotless for other files (#3007)
8651aa73e [examples] Enable NT Flush in Field2d examples (#3013)
78b542737 [build] Add Gazebo build to Actions CI (#3004)
fccf86532 [sim] DriverStationGui: Fix two bugs (#3010)
185741760 [sim] WSProvider_Joystick: Fix off-by-1 in incoming buttons (#3011)
ee7114a58 [glass] Add drive class widgets (#2975)
00fa91d0d [glass] Use ImGui style for gyro widget colors (#3009)
b7a25bfc3 ThirdPartyNotices: Add portable file dialogs license (#3005)
a2e46b9a1 [glass] modernize-use-nullptr (NFC) (#3006)
a751fa22d [build] Apply spotless for java formatting (#1768)
e563a0b7d [wpimath] Make LinearSystemLoop move-constructible and move-assignable (#2967)
49085ca94 [glass] Add context menus to remove and add NetworkTables values (#2979)
560a850a2 [glass] Add NetworkTables Log window (#2997)
66782e231 [sim] Create Left/Right drivetrain current accessors (#3001)
b60eb1544 clang-tidy: bugprone-virtual-near-miss
cbe59fa3b clang-tidy: google-explicit-constructor
c97c6dc06 clang-tidy: google-readability-casting (NFC)
32fa97d68 clang-tidy: modernize-use-nullptr (NFC)
aee460326 clang-tidy: modernize-pass-by-value
29c7da5f1 clang-tidy: modernize-make-unique
6131f4e32 clang-tidy: modernize-concat-nested-namespaces (NFC)
67e03e625 clang-tidy: modernize-use-equals-default
b124f9101 clang-tidy: modernize-use-default-member-init
d11a3a638 clang-tidy: modernize-use-override (NFC)
4cc0706b0 clang-tidy: modernize-use-using (NFC)
885f5a978 [wpilibc] Speed up ScopedTracerTest (#2999)
60b596457 [wpilibj] Fix typos (NFC) (#3000)
6e1919414 [build] Bring naming checkstyle rules up to date with Google Style guide (#1781)
8c8ec5e63 [wpilibj] Suppress unchecked cast warnings (#2995)
b8413ddd5 [wpiutil] Add noexcept to timestamp static functions (#2994)
5d976b6e1 [glass] Load NetworkTableView settings on first draw (#2993)
2b4317452 Replace NOLINT(runtime/explicit) comments with NOLINT (NFC) (#2992)
1c3011ba4 [glass] Fix handling of "/" NetworkTables key (#2991)
574a42f3b [hal] Fix UnsafeManipulateDIO status check (#2987)
9005cd59e [wpilib] Clamp input voltage in sim classes (#2955)
dd494d4ab [glass] NetworkTablesModel::Update(): Avoid use-after-move (#2988)
7cca469a1 [wpimath] NormalizeAngle: Make inline, remove unnamed namespace (#2986)
2aed432b4 Add braces to C++ single-line loops and conditionals (NFC) (#2973)
0291a3ff5 [wpiutil] StringRef: Add noexcept to several constructors (#2984)
5d7315280 [wpimath] Update UnitsTest.cpp copyright (#2985)
254931b9a [wpimath] Remove LinearSystem from LinearSystemLoop (#2968)
aa89744c9 Update OtherVersions.md to include wpimath info (#2983)
1cda3f5ad [glass] Fix styleguide (#2976)
8f1f64ffb Remove year from file copyright message (NFC) (#2972)
2bc0a7795 [examples] Fix wpiformat warning about utility include (#2971)
4204da6ad [glass] Add application icon
7ac39b10f [wpigui] Add icon support
6b567e006 [wpimath] Add support for varying vision standard deviations in pose estimators (#2956)
df299d6ed [wpimath] Add UnscentedKalmanFilter::Correct() overload (#2966)
4e34f0523 [examples] Use ADXRS450_GyroSim class in simulation example (#2964)
9962f6fd7 [wpilib] Give Field2d a default Sendable name (#2953)
f9d492f4b [sim] GUI: Show "Other Devices" window by default (#2961)
a8bb2ef1c [sim] Fix ADXRS450_GyroSim and DutyCycleEncoderSim (#2963)
240c629cd [sim] Try to guess "Map Gamepad" setting (#2960)
952567dd3 [wpilibc] Add missing move constructors and assignment operators (#2959)
10b396b4c [sim] Various WebSockets fixes and enhancements (#2952)
699bbe21a [examples] Fix comments in Gearsbot to match implementation (NFC) (#2957)
27b67deca [glass] Add more widgets (#2947)
581b7ec55 [wpilib] Add option to flush NetworkTables every iterative loop
acfbb1a44 [ntcore] DispatcherBase::Flush: Use wpi::Now()
d85a6d8fe [ntcore] Reduce limit on flush and update rate to 5 ms
20fbb5c63 [sim] Fix stringop truncation warning from GCC 10 (#2945)
1051a06a7 [glass] Show NT timestamps in seconds (#2944)
98dfc2620 [glass] Fix plots (#2943)
1ba0a2ced [sim] GUI: Add keyboard virtual joystick support (#2940)
4afb13f98 [examples] Replace M_PI with wpi::math::pi (#2938)
b27d33675 [examples] Enhance Romi templates (#2931)
00b9ae77f [sim] Change default WS port number to 3300 (#2932)
65219f309 [examples] Update Field2d position in periodic() (#2928)
f78d1d434 [sim] Process WS Encoder reset internally (#2927)
941edca59 [hal] Add Java SimDeviceDataJNI.getSimDeviceName (#2924)
a699435ed [wpilibj] Fix FlywheelSim argument order in constructor (#2922)
66d641718 [examples] Add tasks to run Java examples (#2920)
558e37c41 [examples] Add simple differential drive simulation example (#2918)
4f40d991e [glass] Switch name of Glass back to glass (#2919)
549af9900 [build] Update native-utils to 2021.0.6 (#2914)
b33693009 [glass] Change basename of glass to Glass (#2915)
c9a0edfb8 [glass] Package macOS application bundle
2c5668af4 [wpigui] Add platform-specific preferences save
751dea32a [wpilibc] Try to work around ABI break introduced in #2901 (#2917)
cd8f4bfb1 [build] Package up msvc runtime into maven artifact (#2913)
a6cfcc686 [wpilibc] Move SendableChooser Doxygen comments to header (NFC) (#2911)
b8c4f603d [wpimath] Upgrade to Eigen 3.3.9 (#2910)
0075e4b39 [wpilibj] Fix NPE in Field2d (#2909)
125af556c [simulation] Fix halsim_gui ntcore and wpiutil deps (#2908)
963ad5c25 [wpilib] Add noise to Differential Drive simulator (#2903)
387f56cb7 [examples] Add Romi reference Java example and templates (#2905)
b3deda38c [examples] Zero motors on disabledInit() in sim physics examples (#2906)
2a5ca7745 [glass] Add glass: an application for display of robot data
727940d84 [wpilib] Move Field2d to SmartDashboard
8cd42478e [wpilib] SendableBuilder: Make GetTable() visible
c11d34b26 [command] Use addCommands in command group templates (#2900)
339d7445b [sim] Add HAL hooks for simulationPeriodic (#2881)
d16f05f2c [wpilib] Fix SmartDashboard update order (#2896)
5427b32a4 [wpiutil] unique_function: Restrict implicit conversion (#2899)
f73701239 [ntcore] Add missing SetDefault initializer_list functions (#2898)
f5a6fc070 [sim] Add initialized flag for all solenoids on a PCM (#2897)
bdf5ba91a [wpilibj] Fix typo in ElevatorSim (#2895)
bc8f33877 [wpilib] Add pose estimators (#2867)
3413bfc06 [wpilib] PIDController: Recompute the error in AtSetpoint() (#2822)
2056f0ce0 [wpilib] Fix bugs in Hatchbot examples (#2893)
5eb8cfd69 [wpilibc] Fix MatchDataSender (#2892)
e6a425448 [build] Delete test folders after tests execute (#2891)
d478ad00d [imgui] Allow usage of imgui_stdlib (#2889)
53eda861d [build] Add unit-testing infrastructure to examples (#2863)
cc1d86ba6 [sim] Add title to simulator GUI window (#2888)
f0528f00e [build] CMake: Use project-specific binary and source dirs (#2886)
5cd2ad124 [wpilibc] Add Color::operator!= (#2887)
6c00e7a90 [build] CI CMake: build with GUI enabled (#2884)
53170bbb5 Update roboRIO toolchain installation instructions (#2883)
467258e05 [sim] GUI: Add option to not zero disconnected joysticks (#2876)
129be23c9 Clarify JDK installation instructions in readme (#2882)
8e9290e86 [build] Add separate CMake setting for wpimath (#2885)
7cf5bebf8 [wpilibj] Cache NT writes from DriverStation (#2780)
f7f9087fb [command] Fix timing issue in RamseteCommand (#2871)
256e7904f [wpilibj] SimDeviceSim: Fix sim value changed callback (#2880)
c8ea1b6c3 [wpilib] Add function to adjust LQR controller gain for pure time delay (#2878)
2816b06c0 [sim] HAL_GetControlWord: Fully zero output (#2873)
4c695ea08 Add toolchain installation instructions to README (#2875)
a14d51806 [wpimath] DCMotor: fix doc typo (NFC) (#2868)
017097791 [build] CMake: build sim extensions as shared libs (#2866)
f61726b5a [build] Fix cmake-config files (#2865)
fc27fdac5 [wpilibc] Cache NT values from driver station (#2768)
47c59859e [sim] Make SimDevice callbacks synchronous (#2861)
6e76ab9c0 [build] Turn on WITH_GUI for Windows cmake CI
5f78b7670 [build] Set GLFW_INSTALL to OFF
5e0808c84 [wpigui] Fix Windows cmake build
508f05a47 [imgui] Fix typo in Windows CMake target sources

Change-Id: I1737b45965f31803a96676bedc7dc40e337aa321
git-subtree-dir: third_party/allwpilib
git-subtree-split: e473a00f9785f9949e5ced30901baeaf426d2fc9
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/wpilibc/src/test/native/cpp/AnalogPotentiometerTest.cpp b/wpilibc/src/test/native/cpp/AnalogPotentiometerTest.cpp
new file mode 100644
index 0000000..de69894
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/AnalogPotentiometerTest.cpp
@@ -0,0 +1,92 @@
+// 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 <hal/HAL.h>
+
+#include "frc/AnalogPotentiometer.h"
+#include "frc/simulation/AnalogInputSim.h"
+#include "frc/simulation/RoboRioSim.h"
+#include "gtest/gtest.h"
+
+namespace frc {
+using namespace frc::sim;
+TEST(AnalogPotentiometerTest, InitializeWithAnalogInput) {
+  HAL_Initialize(500, 0);
+
+  AnalogInput ai{0};
+  AnalogPotentiometer pot{&ai};
+  AnalogInputSim sim{ai};
+
+  RoboRioSim::ResetData();
+  sim.SetVoltage(4.0);
+  EXPECT_EQ(0.8, pot.Get());
+}
+
+TEST(AnalogPotentiometerTest, InitializeWithAnalogInputAndScale) {
+  HAL_Initialize(500, 0);
+
+  AnalogInput ai{0};
+  AnalogPotentiometer pot{&ai, 270.0};
+  RoboRioSim::ResetData();
+  AnalogInputSim sim{ai};
+
+  sim.SetVoltage(5.0);
+  EXPECT_EQ(270.0, pot.Get());
+
+  sim.SetVoltage(2.5);
+  EXPECT_EQ(135, pot.Get());
+
+  sim.SetVoltage(0.0);
+  EXPECT_EQ(0.0, pot.Get());
+}
+
+TEST(AnalogPotentiometerTest, InitializeWithChannel) {
+  HAL_Initialize(500, 0);
+
+  AnalogPotentiometer pot{1};
+  AnalogInputSim sim{1};
+
+  sim.SetVoltage(5.0);
+  EXPECT_EQ(1.0, pot.Get());
+}
+
+TEST(AnalogPotentiometerTest, InitializeWithChannelAndScale) {
+  HAL_Initialize(500, 0);
+
+  AnalogPotentiometer pot{1, 180.0};
+  RoboRioSim::ResetData();
+  AnalogInputSim sim{1};
+
+  sim.SetVoltage(5.0);
+  EXPECT_EQ(180.0, pot.Get());
+
+  sim.SetVoltage(0.0);
+  EXPECT_EQ(0.0, pot.Get());
+}
+
+TEST(AnalogPotentiometerTest, WithModifiedBatteryVoltage) {
+  AnalogPotentiometer pot{1, 180.0, 90.0};
+  RoboRioSim::ResetData();
+  AnalogInputSim sim{1};
+
+  // Test at 5v
+  sim.SetVoltage(5.0);
+  EXPECT_EQ(270, pot.Get());
+
+  sim.SetVoltage(0.0);
+  EXPECT_EQ(90, pot.Get());
+
+  // Simulate a lower battery voltage
+  RoboRioSim::SetUserVoltage5V(units::volt_t{2.5});
+
+  sim.SetVoltage(2.5);
+  EXPECT_EQ(270, pot.Get());
+
+  sim.SetVoltage(2.0);
+  EXPECT_EQ(234, pot.Get());
+
+  sim.SetVoltage(0.0);
+  EXPECT_EQ(90, pot.Get());
+}
+}  // namespace frc
diff --git a/wpilibc/src/test/native/cpp/DebouncerTest.cpp b/wpilibc/src/test/native/cpp/DebouncerTest.cpp
new file mode 100644
index 0000000..379a6d5
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/DebouncerTest.cpp
@@ -0,0 +1,48 @@
+// 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/Debouncer.h"  // NOLINT(build/include_order)
+
+#include "frc/simulation/SimHooks.h"
+#include "gtest/gtest.h"
+
+using namespace frc;
+
+TEST(DebouncerTest, DebounceRising) {
+  Debouncer debouncer{20_ms};
+
+  debouncer.Calculate(false);
+  EXPECT_FALSE(debouncer.Calculate(true));
+
+  frc::sim::StepTiming(100_ms);
+
+  EXPECT_TRUE(debouncer.Calculate(true));
+}
+
+TEST(DebouncerTest, DebounceFalling) {
+  Debouncer debouncer{20_ms, Debouncer::DebounceType::kFalling};
+
+  debouncer.Calculate(true);
+  EXPECT_TRUE(debouncer.Calculate(false));
+
+  frc::sim::StepTiming(100_ms);
+
+  EXPECT_FALSE(debouncer.Calculate(false));
+}
+
+TEST(DebouncerTest, DebounceBoth) {
+  Debouncer debouncer{20_ms, Debouncer::DebounceType::kBoth};
+
+  debouncer.Calculate(false);
+  EXPECT_FALSE(debouncer.Calculate(true));
+
+  frc::sim::StepTiming(100_ms);
+
+  EXPECT_TRUE(debouncer.Calculate(true));
+  EXPECT_TRUE(debouncer.Calculate(false));
+
+  frc::sim::StepTiming(100_ms);
+
+  EXPECT_FALSE(debouncer.Calculate(false));
+}
diff --git a/wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp b/wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp
new file mode 100644
index 0000000..2678395
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/DoubleSolenoidTestCTRE.cpp
@@ -0,0 +1,75 @@
+// 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 <hal/HAL.h>
+
+#include "frc/DoubleSolenoid.h"
+#include "frc/PneumaticsControlModule.h"
+#include "frc/Solenoid.h"
+#include "gtest/gtest.h"
+
+namespace frc {
+
+TEST(DoubleSolenoidCTRETest, ValidInitialization) {
+  DoubleSolenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2, 3};
+  solenoid.Set(DoubleSolenoid::kReverse);
+  EXPECT_EQ(DoubleSolenoid::kReverse, solenoid.Get());
+
+  solenoid.Set(DoubleSolenoid::kForward);
+  EXPECT_EQ(DoubleSolenoid::kForward, solenoid.Get());
+
+  solenoid.Set(DoubleSolenoid::kOff);
+  EXPECT_EQ(DoubleSolenoid::kOff, solenoid.Get());
+}
+
+TEST(DoubleSolenoidCTRETest, ThrowForwardPortAlreadyInitialized) {
+  // Single solenoid that is reused for forward port
+  Solenoid solenoid{5, frc::PneumaticsModuleType::CTREPCM, 2};
+  EXPECT_THROW(DoubleSolenoid(5, frc::PneumaticsModuleType::CTREPCM, 2, 3),
+               std::runtime_error);
+}
+
+TEST(DoubleSolenoidCTRETest, ThrowReversePortAlreadyInitialized) {
+  // Single solenoid that is reused for forward port
+  Solenoid solenoid{6, frc::PneumaticsModuleType::CTREPCM, 3};
+  EXPECT_THROW(DoubleSolenoid(6, frc::PneumaticsModuleType::CTREPCM, 2, 3),
+               std::runtime_error);
+}
+
+TEST(DoubleSolenoidCTRETest, ThrowBothPortsAlreadyInitialized) {
+  PneumaticsControlModule pcm{6};
+  // Single solenoid that is reused for forward port
+  Solenoid solenoid0(6, frc::PneumaticsModuleType::CTREPCM, 2);
+  Solenoid solenoid1(6, frc::PneumaticsModuleType::CTREPCM, 3);
+  EXPECT_THROW(DoubleSolenoid(6, frc::PneumaticsModuleType::CTREPCM, 2, 3),
+               std::runtime_error);
+}
+
+TEST(DoubleSolenoidCTRETest, Toggle) {
+  DoubleSolenoid solenoid{4, frc::PneumaticsModuleType::CTREPCM, 2, 3};
+  // Bootstrap it into reverse
+  solenoid.Set(DoubleSolenoid::kReverse);
+
+  solenoid.Toggle();
+  EXPECT_EQ(DoubleSolenoid::kForward, solenoid.Get());
+
+  solenoid.Toggle();
+  EXPECT_EQ(DoubleSolenoid::kReverse, solenoid.Get());
+
+  // Of shouldn't do anything on toggle
+  solenoid.Set(DoubleSolenoid::kOff);
+  solenoid.Toggle();
+  EXPECT_EQ(DoubleSolenoid::kOff, solenoid.Get());
+}
+
+TEST(DoubleSolenoidCTRETest, InvalidForwardPort) {
+  EXPECT_THROW(DoubleSolenoid(0, frc::PneumaticsModuleType::CTREPCM, 100, 1),
+               std::runtime_error);
+}
+
+TEST(DoubleSolenoidCTRETest, InvalidReversePort) {
+  EXPECT_THROW(DoubleSolenoid(0, frc::PneumaticsModuleType::CTREPCM, 0, 100),
+               std::runtime_error);
+}
+}  // namespace frc
diff --git a/wpilibc/src/test/native/cpp/DoubleSolenoidTestREV.cpp b/wpilibc/src/test/native/cpp/DoubleSolenoidTestREV.cpp
new file mode 100644
index 0000000..23893c4
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/DoubleSolenoidTestREV.cpp
@@ -0,0 +1,75 @@
+// 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 <hal/HAL.h>
+
+#include "frc/DoubleSolenoid.h"
+#include "frc/PneumaticsControlModule.h"
+#include "frc/Solenoid.h"
+#include "gtest/gtest.h"
+
+namespace frc {
+
+TEST(DoubleSolenoidREVTest, ValidInitialization) {
+  DoubleSolenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2, 3};
+  solenoid.Set(DoubleSolenoid::kReverse);
+  EXPECT_EQ(DoubleSolenoid::kReverse, solenoid.Get());
+
+  solenoid.Set(DoubleSolenoid::kForward);
+  EXPECT_EQ(DoubleSolenoid::kForward, solenoid.Get());
+
+  solenoid.Set(DoubleSolenoid::kOff);
+  EXPECT_EQ(DoubleSolenoid::kOff, solenoid.Get());
+}
+
+TEST(DoubleSolenoidREVTest, ThrowForwardPortAlreadyInitialized) {
+  // Single solenoid that is reused for forward port
+  Solenoid solenoid{5, frc::PneumaticsModuleType::CTREPCM, 2};
+  EXPECT_THROW(DoubleSolenoid(5, frc::PneumaticsModuleType::CTREPCM, 2, 3),
+               std::runtime_error);
+}
+
+TEST(DoubleSolenoidREVTest, ThrowReversePortAlreadyInitialized) {
+  // Single solenoid that is reused for forward port
+  Solenoid solenoid{6, frc::PneumaticsModuleType::CTREPCM, 3};
+  EXPECT_THROW(DoubleSolenoid(6, frc::PneumaticsModuleType::CTREPCM, 2, 3),
+               std::runtime_error);
+}
+
+TEST(DoubleSolenoidREVTest, ThrowBothPortsAlreadyInitialized) {
+  PneumaticsControlModule pcm{6};
+  // Single solenoid that is reused for forward port
+  Solenoid solenoid0(6, frc::PneumaticsModuleType::CTREPCM, 2);
+  Solenoid solenoid1(6, frc::PneumaticsModuleType::CTREPCM, 3);
+  EXPECT_THROW(DoubleSolenoid(6, frc::PneumaticsModuleType::CTREPCM, 2, 3),
+               std::runtime_error);
+}
+
+TEST(DoubleSolenoidREVTest, Toggle) {
+  DoubleSolenoid solenoid{4, frc::PneumaticsModuleType::CTREPCM, 2, 3};
+  // Bootstrap it into reverse
+  solenoid.Set(DoubleSolenoid::kReverse);
+
+  solenoid.Toggle();
+  EXPECT_EQ(DoubleSolenoid::kForward, solenoid.Get());
+
+  solenoid.Toggle();
+  EXPECT_EQ(DoubleSolenoid::kReverse, solenoid.Get());
+
+  // Of shouldn't do anything on toggle
+  solenoid.Set(DoubleSolenoid::kOff);
+  solenoid.Toggle();
+  EXPECT_EQ(DoubleSolenoid::kOff, solenoid.Get());
+}
+
+TEST(DoubleSolenoidREVTest, InvalidForwardPort) {
+  EXPECT_THROW(DoubleSolenoid(0, frc::PneumaticsModuleType::CTREPCM, 100, 1),
+               std::runtime_error);
+}
+
+TEST(DoubleSolenoidREVTest, InvalidReversePort) {
+  EXPECT_THROW(DoubleSolenoid(0, frc::PneumaticsModuleType::CTREPCM, 0, 100),
+               std::runtime_error);
+}
+}  // namespace frc
diff --git a/wpilibc/src/test/native/cpp/DriverStationTest.cpp b/wpilibc/src/test/native/cpp/DriverStationTest.cpp
index 3b8272f..c191e2b 100644
--- a/wpilibc/src/test/native/cpp/DriverStationTest.cpp
+++ b/wpilibc/src/test/native/cpp/DriverStationTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <string>
 #include <tuple>
@@ -14,10 +11,10 @@
 #include "frc/simulation/SimHooks.h"
 #include "gtest/gtest.h"
 
-class IsJoystickConnectedParametersTests
+class IsJoystickConnectedParametersTest
     : public ::testing::TestWithParam<std::tuple<int, int, int, bool>> {};
 
-TEST_P(IsJoystickConnectedParametersTests, IsJoystickConnected) {
+TEST_P(IsJoystickConnectedParametersTest, IsJoystickConnected) {
   frc::sim::DriverStationSim::SetJoystickAxisCount(1, std::get<0>(GetParam()));
   frc::sim::DriverStationSim::SetJoystickButtonCount(1,
                                                      std::get<1>(GetParam()));
@@ -25,50 +22,54 @@
   frc::sim::DriverStationSim::NotifyNewData();
 
   ASSERT_EQ(std::get<3>(GetParam()),
-            frc::DriverStation::GetInstance().IsJoystickConnected(1));
+            frc::DriverStation::IsJoystickConnected(1));
 }
 
-INSTANTIATE_TEST_SUITE_P(IsConnectedTests, IsJoystickConnectedParametersTests,
+INSTANTIATE_TEST_SUITE_P(IsConnectedTests, IsJoystickConnectedParametersTest,
                          ::testing::Values(std::make_tuple(0, 0, 0, false),
                                            std::make_tuple(1, 0, 0, true),
                                            std::make_tuple(0, 1, 0, true),
                                            std::make_tuple(0, 0, 1, true),
                                            std::make_tuple(1, 1, 1, true),
                                            std::make_tuple(4, 10, 1, true)));
-class JoystickConnectionWarningTests
+class JoystickConnectionWarningTest
     : public ::testing::TestWithParam<
           std::tuple<bool, bool, bool, std::string>> {};
 
-TEST_P(JoystickConnectionWarningTests, JoystickConnectionWarnings) {
+TEST_P(JoystickConnectionWarningTest, JoystickConnectionWarnings) {
   // Capture all output to stderr.
   ::testing::internal::CaptureStderr();
 
   // Set FMS and Silence settings
   frc::sim::DriverStationSim::SetFmsAttached(std::get<0>(GetParam()));
   frc::sim::DriverStationSim::NotifyNewData();
-  frc::DriverStation::GetInstance().SilenceJoystickConnectionWarning(
-      std::get<1>(GetParam()));
+  frc::DriverStation::SilenceJoystickConnectionWarning(std::get<1>(GetParam()));
 
   // Create joystick and attempt to retrieve button.
   frc::Joystick joystick(0);
   joystick.GetRawButton(1);
 
   frc::sim::StepTiming(1_s);
-  EXPECT_EQ(
-      frc::DriverStation::GetInstance().IsJoystickConnectionWarningSilenced(),
-      std::get<2>(GetParam()));
-  EXPECT_EQ(::testing::internal::GetCapturedStderr(), std::get<3>(GetParam()));
+  EXPECT_EQ(frc::DriverStation::IsJoystickConnectionWarningSilenced(),
+            std::get<2>(GetParam()));
+  EXPECT_EQ(::testing::internal::GetCapturedStderr().substr(
+                0, std::get<3>(GetParam()).size()),
+            std::get<3>(GetParam()));
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    DriverStation, JoystickConnectionWarningTests,
-    ::testing::Values(std::make_tuple(false, true, true, ""),
-                      std::make_tuple(false, false, false,
-                                      "Joystick Button missing, check if all "
-                                      "controllers are plugged in\n"),
-                      std::make_tuple(true, true, false,
-                                      "Joystick Button missing, check if all "
-                                      "controllers are plugged in\n"),
-                      std::make_tuple(true, false, false,
-                                      "Joystick Button missing, check if all "
-                                      "controllers are plugged in\n")));
+    DriverStationTests, JoystickConnectionWarningTest,
+    ::testing::Values(
+        std::make_tuple(false, true, true, ""),
+        std::make_tuple(
+            false, false, false,
+            "Warning: Joystick Button 1 missing (max 0), check if all "
+            "controllers are plugged in\n"),
+        std::make_tuple(
+            true, true, false,
+            "Warning: Joystick Button 1 missing (max 0), check if all "
+            "controllers are plugged in\n"),
+        std::make_tuple(
+            true, false, false,
+            "Warning: Joystick Button 1 missing (max 0), check if all "
+            "controllers are plugged in\n")));
diff --git a/wpilibc/src/test/native/cpp/InterruptTest.cpp b/wpilibc/src/test/native/cpp/InterruptTest.cpp
new file mode 100644
index 0000000..1bf0aa2
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/InterruptTest.cpp
@@ -0,0 +1,45 @@
+// 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 <hal/HAL.h>
+
+#include "frc/AsynchronousInterrupt.h"
+#include "frc/DigitalInput.h"
+#include "frc/Timer.h"
+#include "frc/simulation/DIOSim.h"
+#include "gtest/gtest.h"
+
+namespace frc {
+using namespace frc::sim;
+TEST(InterruptTest, AsynchronousInterrupt) {
+  HAL_Initialize(500, 0);
+
+  std::atomic_int counter{0};
+  std::atomic_bool hasFired{false};
+
+  DigitalInput di{0};
+  AsynchronousInterrupt interrupt{di, [&](bool rising, bool falling) {
+                                    counter++;
+                                    hasFired = true;
+                                  }};
+
+  interrupt.Enable();
+  frc::Wait(0.5_s);
+  DIOSim digitalSim{di};
+  digitalSim.SetValue(false);
+  frc::Wait(20_ms);
+  digitalSim.SetValue(true);
+  frc::Wait(10_ms);
+
+  int count = 0;
+  while (!hasFired) {
+    frc::Wait(5_ms);
+    count++;
+    ASSERT_TRUE(count < 1000);
+  }
+  ASSERT_EQ(1, counter.load());
+}
+}  // namespace frc
diff --git a/wpilibc/src/test/native/cpp/JoystickTest.cpp b/wpilibc/src/test/native/cpp/JoystickTest.cpp
index 0480b76..5697879 100644
--- a/wpilibc/src/test/native/cpp/JoystickTest.cpp
+++ b/wpilibc/src/test/native/cpp/JoystickTest.cpp
@@ -1,39 +1,20 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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/Joystick.h"  // NOLINT(build/include_order)
 
+#include "JoystickTestMacros.h"
 #include "frc/simulation/JoystickSim.h"
 #include "gtest/gtest.h"
 
 using namespace frc;
 
-TEST(JoystickTests, GetX) {
-  Joystick joy{1};
-  sim::JoystickSim joysim{joy};
+AXIS_TEST(Joystick, X)
+AXIS_TEST(Joystick, Y)
+AXIS_TEST(Joystick, Z)
+AXIS_TEST(Joystick, Throttle)
+AXIS_TEST(Joystick, Twist)
 
-  joysim.SetX(0.25);
-  joysim.NotifyNewData();
-  ASSERT_NEAR(joy.GetX(), 0.25, 0.001);
-
-  joysim.SetX(0);
-  joysim.NotifyNewData();
-  ASSERT_NEAR(joy.GetX(), 0, 0.001);
-}
-
-TEST(JoystickTests, GetY) {
-  Joystick joy{1};
-  sim::JoystickSim joysim{joy};
-
-  joysim.SetY(0.25);
-  joysim.NotifyNewData();
-  ASSERT_NEAR(joy.GetY(), 0.25, 0.001);
-
-  joysim.SetY(0);
-  joysim.NotifyNewData();
-  ASSERT_NEAR(joy.GetY(), 0, 0.001);
-}
+BUTTON_TEST(Joystick, Trigger)
+BUTTON_TEST(Joystick, Top)
diff --git a/wpilibc/src/test/native/cpp/JoystickTestMacros.h b/wpilibc/src/test/native/cpp/JoystickTestMacros.h
new file mode 100644
index 0000000..81ccedc
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/JoystickTestMacros.h
@@ -0,0 +1,39 @@
+// 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
+
+#define AXIS_TEST(JoystickType, AxisName)          \
+  TEST(JoystickType##Test, Get##AxisName) {        \
+    JoystickType joy{2};                           \
+    sim::JoystickType##Sim joysim{joy};            \
+    joysim.Set##AxisName(0.35);                    \
+    joysim.NotifyNewData();                        \
+    ASSERT_NEAR(joy.Get##AxisName(), 0.35, 0.001); \
+  }
+
+#define BUTTON_TEST(JoystickType, ButtonName)              \
+  TEST(JoystickType##Test, Get##ButtonName) {              \
+    JoystickType joy{1};                                   \
+    sim::JoystickType##Sim joysim{joy};                    \
+                                                           \
+    joysim.Set##ButtonName(false);                         \
+    joysim.NotifyNewData();                                \
+    ASSERT_FALSE(joy.Get##ButtonName());                   \
+    /* need to call pressed and released to clear flags */ \
+    joy.Get##ButtonName##Pressed();                        \
+    joy.Get##ButtonName##Released();                       \
+                                                           \
+    joysim.Set##ButtonName(true);                          \
+    joysim.NotifyNewData();                                \
+    ASSERT_TRUE(joy.Get##ButtonName());                    \
+    ASSERT_TRUE(joy.Get##ButtonName##Pressed());           \
+    ASSERT_FALSE(joy.Get##ButtonName##Released());         \
+                                                           \
+    joysim.Set##ButtonName(false);                         \
+    joysim.NotifyNewData();                                \
+    ASSERT_FALSE(joy.Get##ButtonName());                   \
+    ASSERT_FALSE(joy.Get##ButtonName##Pressed());          \
+    ASSERT_TRUE(joy.Get##ButtonName##Released());          \
+  }
diff --git a/wpilibc/src/test/native/cpp/MockMotorController.cpp b/wpilibc/src/test/native/cpp/MockMotorController.cpp
new file mode 100644
index 0000000..62fd54a
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/MockMotorController.cpp
@@ -0,0 +1,31 @@
+// 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 "MockMotorController.h"
+
+using namespace frc;
+
+void MockMotorController::Set(double speed) {
+  m_speed = m_isInverted ? -speed : speed;
+}
+
+double MockMotorController::Get() const {
+  return m_speed;
+}
+
+void MockMotorController::SetInverted(bool isInverted) {
+  m_isInverted = isInverted;
+}
+
+bool MockMotorController::GetInverted() const {
+  return m_isInverted;
+}
+
+void MockMotorController::Disable() {
+  m_speed = 0;
+}
+
+void MockMotorController::StopMotor() {
+  Disable();
+}
diff --git a/wpilibc/src/test/native/cpp/MockSpeedController.cpp b/wpilibc/src/test/native/cpp/MockSpeedController.cpp
deleted file mode 100644
index 875fec1..0000000
--- a/wpilibc/src/test/native/cpp/MockSpeedController.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2018 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "MockSpeedController.h"
-
-using namespace frc;
-
-void MockSpeedController::Set(double speed) {
-  m_speed = m_isInverted ? -speed : speed;
-}
-
-double MockSpeedController::Get() const { return m_speed; }
-
-void MockSpeedController::SetInverted(bool isInverted) {
-  m_isInverted = isInverted;
-}
-
-bool MockSpeedController::GetInverted() const { return m_isInverted; }
-
-void MockSpeedController::Disable() { m_speed = 0; }
-
-void MockSpeedController::StopMotor() { Disable(); }
-
-void MockSpeedController::PIDWrite(double output) { Set(output); }
diff --git a/wpilibc/src/test/native/cpp/PS4ControllerTest.cpp b/wpilibc/src/test/native/cpp/PS4ControllerTest.cpp
new file mode 100644
index 0000000..329a165
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/PS4ControllerTest.cpp
@@ -0,0 +1,37 @@
+// 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/PS4Controller.h"  // NOLINT(build/include_order)
+
+#include "JoystickTestMacros.h"
+#include "frc/simulation/PS4ControllerSim.h"
+#include "gtest/gtest.h"
+
+using namespace frc;
+
+BUTTON_TEST(PS4Controller, SquareButton)
+BUTTON_TEST(PS4Controller, CrossButton)
+BUTTON_TEST(PS4Controller, CircleButton)
+BUTTON_TEST(PS4Controller, TriangleButton)
+
+BUTTON_TEST(PS4Controller, L1Button)
+BUTTON_TEST(PS4Controller, R1Button)
+BUTTON_TEST(PS4Controller, L2Button)
+BUTTON_TEST(PS4Controller, R2Button)
+
+BUTTON_TEST(PS4Controller, ShareButton)
+BUTTON_TEST(PS4Controller, OptionsButton)
+
+BUTTON_TEST(PS4Controller, L3Button)
+BUTTON_TEST(PS4Controller, R3Button)
+
+BUTTON_TEST(PS4Controller, PSButton)
+BUTTON_TEST(PS4Controller, Touchpad)
+
+AXIS_TEST(PS4Controller, LeftX)
+AXIS_TEST(PS4Controller, RightX)
+AXIS_TEST(PS4Controller, LeftY)
+AXIS_TEST(PS4Controller, RightY)
+AXIS_TEST(PS4Controller, L2Axis)
+AXIS_TEST(PS4Controller, R2Axis)
diff --git a/wpilibc/src/test/native/cpp/ScopedTracerTest.cpp b/wpilibc/src/test/native/cpp/ScopedTracerTest.cpp
index da222de..c4ef317 100644
--- a/wpilibc/src/test/native/cpp/ScopedTracerTest.cpp
+++ b/wpilibc/src/test/native/cpp/ScopedTracerTest.cpp
@@ -1,33 +1,28 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <string>
-#include <thread>
+#include <string_view>
 
 #include <wpi/SmallString.h>
-#include <wpi/StringRef.h>
+#include <wpi/StringExtras.h>
 #include <wpi/raw_ostream.h>
 
 #include "frc/ScopedTracer.h"
+#include "frc/simulation/SimHooks.h"
 #include "gtest/gtest.h"
 
-wpi::SmallString<128> buf;
-wpi::raw_svector_ostream os(buf);
-
-#ifdef __APPLE__
-TEST(ScopedTracerTest, DISABLED_Timing) {
-#else
 TEST(ScopedTracerTest, Timing) {
-#endif
+  wpi::SmallString<128> buf;
+  wpi::raw_svector_ostream os(buf);
+
+  frc::sim::PauseTiming();
   {
     frc::ScopedTracer tracer("timing_test", os);
-    std::this_thread::sleep_for(std::chrono::milliseconds(1500));
+    frc::sim::StepTiming(1.5_s);
   }
+  frc::sim::ResumeTiming();
 
-  wpi::StringRef out = os.str();
-  EXPECT_TRUE(out.startswith("	timing_test: 1.5"));
+  std::string_view out = os.str();
+  EXPECT_TRUE(wpi::starts_with(out, "	timing_test: 1.5"));
 }
diff --git a/wpilibc/src/test/native/cpp/SlewRateLimiterTest.cpp b/wpilibc/src/test/native/cpp/SlewRateLimiterTest.cpp
deleted file mode 100644
index dea56bb..0000000
--- a/wpilibc/src/test/native/cpp/SlewRateLimiterTest.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include <thread>
-
-#include <units/length.h>
-#include <units/time.h>
-#include <units/velocity.h>
-
-#include "frc/SlewRateLimiter.h"
-#include "frc/simulation/SimHooks.h"
-#include "gtest/gtest.h"
-
-TEST(SlewRateLimiterTest, SlewRateLimitTest) {
-  frc::SlewRateLimiter<units::meters> limiter(1_mps);
-
-  frc::sim::StepTiming(1.0_s);
-
-  EXPECT_TRUE(limiter.Calculate(2_m) < 2_m);
-}
-
-TEST(SlewRateLimiterTest, SlewRateNoLimitTest) {
-  frc::SlewRateLimiter<units::meters> limiter(1_mps);
-
-  frc::sim::StepTiming(1.0_s);
-
-  EXPECT_EQ(limiter.Calculate(0.5_m), 0.5_m);
-}
diff --git a/wpilibc/src/test/native/cpp/SolenoidTestCTRE.cpp b/wpilibc/src/test/native/cpp/SolenoidTestCTRE.cpp
new file mode 100644
index 0000000..61e3fb6
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/SolenoidTestCTRE.cpp
@@ -0,0 +1,52 @@
+// 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 <hal/HAL.h>
+
+#include "frc/DoubleSolenoid.h"
+#include "frc/PneumaticsControlModule.h"
+#include "frc/Solenoid.h"
+#include "gtest/gtest.h"
+
+namespace frc {
+TEST(SolenoidCTRETest, ValidInitialization) {
+  Solenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2};
+  EXPECT_EQ(2, solenoid.GetChannel());
+
+  solenoid.Set(true);
+  EXPECT_TRUE(solenoid.Get());
+
+  solenoid.Set(false);
+  EXPECT_FALSE(solenoid.Get());
+}
+
+TEST(SolenoidCTRETest, DoubleInitialization) {
+  Solenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2};
+  EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::CTREPCM, 2),
+               std::runtime_error);
+}
+
+TEST(SolenoidCTRETest, DoubleInitializationFromDoubleSolenoid) {
+  DoubleSolenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2, 3};
+  EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::CTREPCM, 2),
+               std::runtime_error);
+}
+
+TEST(SolenoidCTRETest, InvalidChannel) {
+  EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::CTREPCM, 100),
+               std::runtime_error);
+}
+
+TEST(SolenoidCTRETest, Toggle) {
+  Solenoid solenoid{3, frc::PneumaticsModuleType::CTREPCM, 2};
+  solenoid.Set(true);
+  EXPECT_TRUE(solenoid.Get());
+
+  solenoid.Toggle();
+  EXPECT_FALSE(solenoid.Get());
+
+  solenoid.Toggle();
+  EXPECT_TRUE(solenoid.Get());
+}
+}  // namespace frc
diff --git a/wpilibc/src/test/native/cpp/SolenoidTestREV.cpp b/wpilibc/src/test/native/cpp/SolenoidTestREV.cpp
new file mode 100644
index 0000000..75ea261
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/SolenoidTestREV.cpp
@@ -0,0 +1,52 @@
+// 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 <hal/HAL.h>
+
+#include "frc/DoubleSolenoid.h"
+#include "frc/PneumaticsControlModule.h"
+#include "frc/Solenoid.h"
+#include "gtest/gtest.h"
+
+namespace frc {
+TEST(SolenoidREVTest, ValidInitialization) {
+  Solenoid solenoid{3, frc::PneumaticsModuleType::REVPH, 2};
+  EXPECT_EQ(2, solenoid.GetChannel());
+
+  solenoid.Set(true);
+  EXPECT_TRUE(solenoid.Get());
+
+  solenoid.Set(false);
+  EXPECT_FALSE(solenoid.Get());
+}
+
+TEST(SolenoidREVTest, DoubleInitialization) {
+  Solenoid solenoid{3, frc::PneumaticsModuleType::REVPH, 2};
+  EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::REVPH, 2),
+               std::runtime_error);
+}
+
+TEST(SolenoidREVTest, DoubleInitializationFromDoubleSolenoid) {
+  DoubleSolenoid solenoid{3, frc::PneumaticsModuleType::REVPH, 2, 3};
+  EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::REVPH, 2),
+               std::runtime_error);
+}
+
+TEST(SolenoidREVTest, InvalidChannel) {
+  EXPECT_THROW(Solenoid(3, frc::PneumaticsModuleType::REVPH, 100),
+               std::runtime_error);
+}
+
+TEST(SolenoidREVTest, Toggle) {
+  Solenoid solenoid{3, frc::PneumaticsModuleType::REVPH, 2};
+  solenoid.Set(true);
+  EXPECT_TRUE(solenoid.Get());
+
+  solenoid.Toggle();
+  EXPECT_FALSE(solenoid.Get());
+
+  solenoid.Toggle();
+  EXPECT_TRUE(solenoid.Get());
+}
+}  // namespace frc
diff --git a/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp b/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp
index cb81fc9..38e1592 100644
--- a/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp
+++ b/wpilibc/src/test/native/cpp/SpeedControllerGroupTest.cpp
@@ -1,33 +1,30 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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/SpeedControllerGroup.h"  // NOLINT(build/include_order)
+#include "frc/motorcontrol/MotorControllerGroup.h"  // NOLINT(build/include_order)
 
 #include <memory>
 #include <vector>
 
-#include "MockSpeedController.h"
+#include "MockMotorController.h"
 #include "gtest/gtest.h"
 
 using namespace frc;
 
-enum SpeedControllerGroupTestType { TEST_ONE, TEST_TWO, TEST_THREE };
+enum MotorControllerGroupTestType { TEST_ONE, TEST_TWO, TEST_THREE };
 
 std::ostream& operator<<(std::ostream& os,
-                         const SpeedControllerGroupTestType& type) {
+                         const MotorControllerGroupTestType& type) {
   switch (type) {
     case TEST_ONE:
-      os << "SpeedControllerGroup with one speed controller";
+      os << "MotorControllerGroup with one speed controller";
       break;
     case TEST_TWO:
-      os << "SpeedControllerGroup with two speed controllers";
+      os << "MotorControllerGroup with two speed controllers";
       break;
     case TEST_THREE:
-      os << "SpeedControllerGroup with three speed controllers";
+      os << "MotorControllerGroup with three speed controllers";
       break;
   }
 
@@ -35,26 +32,26 @@
 }
 
 /**
- * A fixture used for SpeedControllerGroup testing.
+ * A fixture used for MotorControllerGroup testing.
  */
-class SpeedControllerGroupTest
-    : public testing::TestWithParam<SpeedControllerGroupTestType> {
+class MotorControllerGroupTest
+    : public testing::TestWithParam<MotorControllerGroupTestType> {
  protected:
-  std::vector<MockSpeedController> m_speedControllers;
-  std::unique_ptr<SpeedControllerGroup> m_group;
+  std::vector<MockMotorController> m_speedControllers;
+  std::unique_ptr<MotorControllerGroup> m_group;
 
   void SetUp() override {
     switch (GetParam()) {
       case TEST_ONE: {
         m_speedControllers.emplace_back();
-        m_group = std::make_unique<SpeedControllerGroup>(m_speedControllers[0]);
+        m_group = std::make_unique<MotorControllerGroup>(m_speedControllers[0]);
         break;
       }
 
       case TEST_TWO: {
         m_speedControllers.emplace_back();
         m_speedControllers.emplace_back();
-        m_group = std::make_unique<SpeedControllerGroup>(m_speedControllers[0],
+        m_group = std::make_unique<MotorControllerGroup>(m_speedControllers[0],
                                                          m_speedControllers[1]);
         break;
       }
@@ -63,7 +60,7 @@
         m_speedControllers.emplace_back();
         m_speedControllers.emplace_back();
         m_speedControllers.emplace_back();
-        m_group = std::make_unique<SpeedControllerGroup>(m_speedControllers[0],
+        m_group = std::make_unique<MotorControllerGroup>(m_speedControllers[0],
                                                          m_speedControllers[1],
                                                          m_speedControllers[2]);
         break;
@@ -72,7 +69,7 @@
   }
 };
 
-TEST_P(SpeedControllerGroupTest, Set) {
+TEST_P(MotorControllerGroupTest, Set) {
   m_group->Set(1.0);
 
   for (auto& speedController : m_speedControllers) {
@@ -80,13 +77,13 @@
   }
 }
 
-TEST_P(SpeedControllerGroupTest, GetInverted) {
+TEST_P(MotorControllerGroupTest, GetInverted) {
   m_group->SetInverted(true);
 
   EXPECT_TRUE(m_group->GetInverted());
 }
 
-TEST_P(SpeedControllerGroupTest, SetInvertedDoesNotModifySpeedControllers) {
+TEST_P(MotorControllerGroupTest, SetInvertedDoesNotModifyMotorControllers) {
   for (auto& speedController : m_speedControllers) {
     speedController.SetInverted(false);
   }
@@ -97,7 +94,7 @@
   }
 }
 
-TEST_P(SpeedControllerGroupTest, SetInvertedDoesInvert) {
+TEST_P(MotorControllerGroupTest, SetInvertedDoesInvert) {
   m_group->SetInverted(true);
   m_group->Set(1.0);
 
@@ -106,7 +103,7 @@
   }
 }
 
-TEST_P(SpeedControllerGroupTest, Disable) {
+TEST_P(MotorControllerGroupTest, Disable) {
   m_group->Set(1.0);
   m_group->Disable();
 
@@ -115,7 +112,7 @@
   }
 }
 
-TEST_P(SpeedControllerGroupTest, StopMotor) {
+TEST_P(MotorControllerGroupTest, StopMotor) {
   m_group->Set(1.0);
   m_group->StopMotor();
 
@@ -124,13 +121,5 @@
   }
 }
 
-TEST_P(SpeedControllerGroupTest, PIDWrite) {
-  m_group->PIDWrite(1.0);
-
-  for (auto& speedController : m_speedControllers) {
-    EXPECT_FLOAT_EQ(speedController.Get(), 1.0);
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(Test, SpeedControllerGroupTest,
+INSTANTIATE_TEST_SUITE_P(Tests, MotorControllerGroupTest,
                          testing::Values(TEST_ONE, TEST_TWO, TEST_THREE));
diff --git a/wpilibc/src/test/native/cpp/TestCallbackHelpers.cpp b/wpilibc/src/test/native/cpp/TestCallbackHelpers.cpp
new file mode 100644
index 0000000..d98f539
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/TestCallbackHelpers.cpp
@@ -0,0 +1,82 @@
+// 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 "callback_helpers/TestCallbackHelpers.h"
+
+#include <iostream>
+
+#include <fmt/format.h>
+
+namespace frc::sim {
+
+void BooleanCallback::HandleCallback(std::string_view name,
+                                     const HAL_Value* value) {
+  if (!value) {
+    throw std::invalid_argument("Null value");
+  }
+  if (value->type != HAL_BOOLEAN) {
+    throw std::invalid_argument(
+        fmt::format("Wrong type '{}' for boolean", value->type).c_str());
+  }
+  m_wasTriggered = true;
+  m_lastValue = value->data.v_boolean;
+}
+
+void EnumCallback::HandleCallback(std::string_view name,
+                                  const HAL_Value* value) {
+  if (!value) {
+    throw std::invalid_argument("Null value");
+  }
+  if (value->type != HAL_ENUM) {
+    throw std::invalid_argument(
+        fmt::format("Wrong type '{}' for enum", value->type).c_str());
+  }
+
+  m_wasTriggered = true;
+  m_lastValue = value->data.v_enum;
+}
+
+void IntCallback::HandleCallback(std::string_view name,
+                                 const HAL_Value* value) {
+  if (!value) {
+    throw std::invalid_argument("Null value");
+  }
+  if (value->type != HAL_INT) {
+    throw std::invalid_argument(
+        fmt::format("Wrong type '{}' for integer", value->type).c_str());
+  }
+
+  m_wasTriggered = true;
+  m_lastValue = value->data.v_int;
+}
+
+void LongCallback::HandleCallback(std::string_view name,
+                                  const HAL_Value* value) {
+  if (!value) {
+    throw std::invalid_argument("Null value");
+  }
+  if (value->type != HAL_LONG) {
+    throw std::invalid_argument(
+        fmt::format("Wrong type '{}' for long", value->type).c_str());
+  }
+
+  m_wasTriggered = true;
+  m_lastValue = value->data.v_long;
+}
+
+void DoubleCallback::HandleCallback(std::string_view name,
+                                    const HAL_Value* value) {
+  if (!value) {
+    throw std::invalid_argument("Null value");
+  }
+  if (value->type != HAL_DOUBLE) {
+    throw std::invalid_argument(
+        fmt::format("Wrong type '{}' for double", value->type).c_str());
+  }
+
+  m_wasTriggered = true;
+  m_lastValue = value->data.v_double;
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/TimedRobotTest.cpp b/wpilibc/src/test/native/cpp/TimedRobotTest.cpp
index f91efb8..ea9656b 100644
--- a/wpilibc/src/test/native/cpp/TimedRobotTest.cpp
+++ b/wpilibc/src/test/native/cpp/TimedRobotTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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/TimedRobot.h"  // NOLINT(build/include_order)
 
@@ -35,6 +32,11 @@
   std::atomic<uint32_t> m_teleopInitCount{0};
   std::atomic<uint32_t> m_testInitCount{0};
 
+  std::atomic<uint32_t> m_disabledExitCount{0};
+  std::atomic<uint32_t> m_autonomousExitCount{0};
+  std::atomic<uint32_t> m_teleopExitCount{0};
+  std::atomic<uint32_t> m_testExitCount{0};
+
   std::atomic<uint32_t> m_robotPeriodicCount{0};
   std::atomic<uint32_t> m_simulationPeriodicCount{0};
   std::atomic<uint32_t> m_disabledPeriodicCount{0};
@@ -65,10 +67,18 @@
   void TeleopPeriodic() override { m_teleopPeriodicCount++; }
 
   void TestPeriodic() override { m_testPeriodicCount++; }
+
+  void DisabledExit() override { m_disabledExitCount++; }
+
+  void AutonomousExit() override { m_autonomousExitCount++; }
+
+  void TeleopExit() override { m_teleopExitCount++; }
+
+  void TestExit() override { m_testExitCount++; }
 };
 }  // namespace
 
-TEST_F(TimedRobotTest, Disabled) {
+TEST_F(TimedRobotTest, DisabledMode) {
   MockRobot robot;
 
   std::thread robotThread{[&] { robot.StartCompetition(); }};
@@ -91,6 +101,11 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   frc::sim::StepTiming(20_ms);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
@@ -107,6 +122,11 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   frc::sim::StepTiming(20_ms);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
@@ -123,11 +143,16 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   robot.EndCompetition();
   robotThread.join();
 }
 
-TEST_F(TimedRobotTest, Autonomous) {
+TEST_F(TimedRobotTest, AutonomousMode) {
   MockRobot robot;
 
   std::thread robotThread{[&] { robot.StartCompetition(); }};
@@ -152,6 +177,11 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   frc::sim::StepTiming(20_ms);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
@@ -168,6 +198,11 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   frc::sim::StepTiming(20_ms);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
@@ -184,11 +219,16 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   robot.EndCompetition();
   robotThread.join();
 }
 
-TEST_F(TimedRobotTest, Teleop) {
+TEST_F(TimedRobotTest, TeleopMode) {
   MockRobot robot;
 
   std::thread robotThread{[&] { robot.StartCompetition(); }};
@@ -213,6 +253,11 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   frc::sim::StepTiming(20_ms);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
@@ -229,6 +274,11 @@
   EXPECT_EQ(1u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   frc::sim::StepTiming(20_ms);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
@@ -245,11 +295,16 @@
   EXPECT_EQ(2u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   robot.EndCompetition();
   robotThread.join();
 }
 
-TEST_F(TimedRobotTest, Test) {
+TEST_F(TimedRobotTest, TestMode) {
   MockRobot robot;
 
   std::thread robotThread{[&] { robot.StartCompetition(); }};
@@ -274,6 +329,11 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(0u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   frc::sim::StepTiming(20_ms);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
@@ -290,6 +350,11 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(1u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
   frc::sim::StepTiming(20_ms);
 
   EXPECT_EQ(1u, robot.m_robotInitCount);
@@ -306,6 +371,117 @@
   EXPECT_EQ(0u, robot.m_teleopPeriodicCount);
   EXPECT_EQ(2u, robot.m_testPeriodicCount);
 
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
+  robot.EndCompetition();
+  robotThread.join();
+}
+
+TEST_F(TimedRobotTest, ModeChange) {
+  MockRobot robot;
+
+  std::thread robotThread{[&] { robot.StartCompetition(); }};
+
+  // Start in disabled
+  frc::sim::DriverStationSim::SetEnabled(false);
+  frc::sim::DriverStationSim::SetAutonomous(false);
+  frc::sim::DriverStationSim::SetTest(false);
+  frc::sim::DriverStationSim::NotifyNewData();
+  frc::sim::StepTiming(0_ms);  // Wait for Notifiers
+
+  EXPECT_EQ(0u, robot.m_disabledInitCount);
+  EXPECT_EQ(0u, robot.m_autonomousInitCount);
+  EXPECT_EQ(0u, robot.m_teleopInitCount);
+  EXPECT_EQ(0u, robot.m_testInitCount);
+
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
+  frc::sim::StepTiming(20_ms);
+
+  EXPECT_EQ(1u, robot.m_disabledInitCount);
+  EXPECT_EQ(0u, robot.m_autonomousInitCount);
+  EXPECT_EQ(0u, robot.m_teleopInitCount);
+  EXPECT_EQ(0u, robot.m_testInitCount);
+
+  EXPECT_EQ(0u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
+  // Transition to autonomous
+  frc::sim::DriverStationSim::SetEnabled(true);
+  frc::sim::DriverStationSim::SetAutonomous(true);
+  frc::sim::DriverStationSim::SetTest(false);
+
+  frc::sim::StepTiming(20_ms);
+
+  EXPECT_EQ(1u, robot.m_disabledInitCount);
+  EXPECT_EQ(1u, robot.m_autonomousInitCount);
+  EXPECT_EQ(0u, robot.m_teleopInitCount);
+  EXPECT_EQ(0u, robot.m_testInitCount);
+
+  EXPECT_EQ(1u, robot.m_disabledExitCount);
+  EXPECT_EQ(0u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
+  // Transition to teleop
+  frc::sim::DriverStationSim::SetEnabled(true);
+  frc::sim::DriverStationSim::SetAutonomous(false);
+  frc::sim::DriverStationSim::SetTest(false);
+
+  frc::sim::StepTiming(20_ms);
+
+  EXPECT_EQ(1u, robot.m_disabledInitCount);
+  EXPECT_EQ(1u, robot.m_autonomousInitCount);
+  EXPECT_EQ(1u, robot.m_teleopInitCount);
+  EXPECT_EQ(0u, robot.m_testInitCount);
+
+  EXPECT_EQ(1u, robot.m_disabledExitCount);
+  EXPECT_EQ(1u, robot.m_autonomousExitCount);
+  EXPECT_EQ(0u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
+  // Transition to test
+  frc::sim::DriverStationSim::SetEnabled(true);
+  frc::sim::DriverStationSim::SetAutonomous(false);
+  frc::sim::DriverStationSim::SetTest(true);
+
+  frc::sim::StepTiming(20_ms);
+
+  EXPECT_EQ(1u, robot.m_disabledInitCount);
+  EXPECT_EQ(1u, robot.m_autonomousInitCount);
+  EXPECT_EQ(1u, robot.m_teleopInitCount);
+  EXPECT_EQ(1u, robot.m_testInitCount);
+
+  EXPECT_EQ(1u, robot.m_disabledExitCount);
+  EXPECT_EQ(1u, robot.m_autonomousExitCount);
+  EXPECT_EQ(1u, robot.m_teleopExitCount);
+  EXPECT_EQ(0u, robot.m_testExitCount);
+
+  // Transition to disabled
+  frc::sim::DriverStationSim::SetEnabled(false);
+  frc::sim::DriverStationSim::SetAutonomous(false);
+  frc::sim::DriverStationSim::SetTest(false);
+
+  frc::sim::StepTiming(20_ms);
+
+  EXPECT_EQ(2u, robot.m_disabledInitCount);
+  EXPECT_EQ(1u, robot.m_autonomousInitCount);
+  EXPECT_EQ(1u, robot.m_teleopInitCount);
+  EXPECT_EQ(1u, robot.m_testInitCount);
+
+  EXPECT_EQ(1u, robot.m_disabledExitCount);
+  EXPECT_EQ(1u, robot.m_autonomousExitCount);
+  EXPECT_EQ(1u, robot.m_teleopExitCount);
+  EXPECT_EQ(1u, robot.m_testExitCount);
+
   robot.EndCompetition();
   robotThread.join();
 }
diff --git a/wpilibc/src/test/native/cpp/TimerTest.cpp b/wpilibc/src/test/native/cpp/TimerTest.cpp
new file mode 100644
index 0000000..a7adc45
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/TimerTest.cpp
@@ -0,0 +1,115 @@
+// 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 "frc/simulation/SimHooks.h"
+#include "gtest/gtest.h"
+
+using namespace frc;
+
+namespace {
+class TimerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    frc::sim::PauseTiming();
+    frc::sim::RestartTiming();
+  }
+
+  void TearDown() override { frc::sim::ResumeTiming(); }
+};
+
+}  // namespace
+
+TEST_F(TimerTest, StartStop) {
+  Timer timer;
+
+  // Verify timer is initialized as stopped
+  EXPECT_EQ(timer.Get(), 0_s);
+  frc::sim::StepTiming(500_ms);
+  EXPECT_EQ(timer.Get(), 0_s);
+
+  // Verify timer increments after it's started
+  timer.Start();
+  frc::sim::StepTiming(500_ms);
+  EXPECT_EQ(timer.Get(), 500_ms);
+
+  // Verify timer stops incrementing after it's stopped
+  timer.Stop();
+  frc::sim::StepTiming(500_ms);
+  EXPECT_EQ(timer.Get(), 500_ms);
+}
+
+TEST_F(TimerTest, Reset) {
+  Timer timer;
+  timer.Start();
+
+  // Advance timer to 500 ms
+  EXPECT_EQ(timer.Get(), 0_s);
+  frc::sim::StepTiming(500_ms);
+  EXPECT_EQ(timer.Get(), 500_ms);
+
+  // Verify timer reports 0 ms after reset
+  timer.Reset();
+  EXPECT_EQ(timer.Get(), 0_s);
+
+  // Verify timer continues incrementing
+  frc::sim::StepTiming(500_ms);
+  EXPECT_EQ(timer.Get(), 500_ms);
+
+  // Verify timer doesn't start incrementing after reset if it was stopped
+  timer.Stop();
+  timer.Reset();
+  frc::sim::StepTiming(500_ms);
+  EXPECT_EQ(timer.Get(), 0_ms);
+}
+
+TEST_F(TimerTest, HasElapsed) {
+  Timer timer;
+
+  // Verify 0 ms has elapsed since timer hasn't started
+  EXPECT_TRUE(timer.HasElapsed(0_s));
+
+  // Verify timer doesn't report elapsed time when stopped
+  frc::sim::StepTiming(500_ms);
+  EXPECT_FALSE(timer.HasElapsed(400_ms));
+
+  timer.Start();
+
+  // Verify timer reports >= 400 ms has elapsed after multiple calls
+  frc::sim::StepTiming(500_ms);
+  EXPECT_TRUE(timer.HasElapsed(400_ms));
+  EXPECT_TRUE(timer.HasElapsed(400_ms));
+}
+
+TEST_F(TimerTest, AdvanceIfElapsed) {
+  Timer timer;
+
+  // Verify 0 ms has elapsed since timer hasn't started
+  EXPECT_TRUE(timer.AdvanceIfElapsed(0_s));
+
+  // Verify timer doesn't report elapsed time when stopped
+  frc::sim::StepTiming(500_ms);
+  EXPECT_FALSE(timer.AdvanceIfElapsed(400_ms));
+
+  timer.Start();
+
+  // Verify timer reports >= 400 ms has elapsed for only first call
+  frc::sim::StepTiming(500_ms);
+  EXPECT_TRUE(timer.AdvanceIfElapsed(400_ms));
+  EXPECT_FALSE(timer.AdvanceIfElapsed(400_ms));
+
+  // Verify timer reports >= 400 ms has elapsed for two calls
+  frc::sim::StepTiming(1_s);
+  EXPECT_TRUE(timer.AdvanceIfElapsed(400_ms));
+  EXPECT_TRUE(timer.AdvanceIfElapsed(400_ms));
+  EXPECT_FALSE(timer.AdvanceIfElapsed(400_ms));
+}
+
+TEST_F(TimerTest, GetFPGATimestamp) {
+  auto start = frc::Timer::GetFPGATimestamp();
+  frc::sim::StepTiming(500_ms);
+  auto end = frc::Timer::GetFPGATimestamp();
+  EXPECT_EQ(start + 500_ms, end);
+}
diff --git a/wpilibc/src/test/native/cpp/TimesliceRobotTest.cpp b/wpilibc/src/test/native/cpp/TimesliceRobotTest.cpp
new file mode 100644
index 0000000..a2e4e13
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/TimesliceRobotTest.cpp
@@ -0,0 +1,100 @@
+// 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/TimesliceRobot.h"  // NOLINT(build/include_order)
+
+#include <stdint.h>
+
+#include <atomic>
+#include <thread>
+
+#include "frc/simulation/DriverStationSim.h"
+#include "frc/simulation/SimHooks.h"
+#include "gtest/gtest.h"
+
+using namespace frc;
+
+namespace {
+class TimesliceRobotTest : public ::testing::Test {
+ protected:
+  void SetUp() override { frc::sim::PauseTiming(); }
+
+  void TearDown() override { frc::sim::ResumeTiming(); }
+};
+
+class MockRobot : public TimesliceRobot {
+ public:
+  std::atomic<uint32_t> m_robotPeriodicCount{0};
+
+  MockRobot() : TimesliceRobot{2_ms, 5_ms} {}
+
+  void RobotPeriodic() override { m_robotPeriodicCount++; }
+};
+}  // namespace
+
+TEST_F(TimesliceRobotTest, Schedule) {
+  MockRobot robot;
+
+  std::atomic<uint32_t> callbackCount1{0};
+  std::atomic<uint32_t> callbackCount2{0};
+
+  // Timeslice allocation table
+  //
+  // |       Name      | Offset (ms) | Allocation (ms)|
+  // |-----------------|-------------|----------------|
+  // | RobotPeriodic() |           0 |              2 |
+  // | Callback 1      |           2 |            0.5 |
+  // | Callback 2      |         2.5 |              1 |
+  robot.Schedule([&] { callbackCount1++; }, 0.5_ms);
+  robot.Schedule([&] { callbackCount2++; }, 1_ms);
+
+  std::thread robotThread{[&] { robot.StartCompetition(); }};
+
+  frc::sim::DriverStationSim::SetEnabled(false);
+  frc::sim::DriverStationSim::NotifyNewData();
+  frc::sim::StepTiming(0_ms);  // Wait for Notifiers
+
+  // Functions scheduled with addPeriodic() are delayed by one period before
+  // their first run (5 ms for this test's callbacks here and 20 ms for
+  // robotPeriodic()).
+  frc::sim::StepTiming(5_ms);
+
+  EXPECT_EQ(0u, robot.m_robotPeriodicCount);
+  EXPECT_EQ(0u, callbackCount1);
+  EXPECT_EQ(0u, callbackCount2);
+
+  // Step to 1.5 ms
+  frc::sim::StepTiming(1.5_ms);
+  EXPECT_EQ(0u, robot.m_robotPeriodicCount);
+  EXPECT_EQ(0u, callbackCount1);
+  EXPECT_EQ(0u, callbackCount2);
+
+  // Step to 2.25 ms
+  frc::sim::StepTiming(0.75_ms);
+  EXPECT_EQ(0u, robot.m_robotPeriodicCount);
+  EXPECT_EQ(1u, callbackCount1);
+  EXPECT_EQ(0u, callbackCount2);
+
+  // Step to 2.75 ms
+  frc::sim::StepTiming(0.5_ms);
+  EXPECT_EQ(0u, robot.m_robotPeriodicCount);
+  EXPECT_EQ(1u, callbackCount1);
+  EXPECT_EQ(1u, callbackCount2);
+
+  robot.EndCompetition();
+  robotThread.join();
+}
+
+TEST_F(TimesliceRobotTest, ScheduleOverrun) {
+  MockRobot robot;
+
+  robot.Schedule([] {}, 0.5_ms);
+  robot.Schedule([] {}, 1_ms);
+
+  // offset = 2 ms + 0.5 ms + 1 ms = 3.5 ms
+  // 3.5 ms + 3 ms allocation = 6.5 ms > max of 5 ms
+  EXPECT_THROW(robot.Schedule([] {}, 3_ms), std::runtime_error);
+
+  robot.EndCompetition();
+}
diff --git a/wpilibc/src/test/native/cpp/UltrasonicTest.cpp b/wpilibc/src/test/native/cpp/UltrasonicTest.cpp
new file mode 100644
index 0000000..72d72d2
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/UltrasonicTest.cpp
@@ -0,0 +1,26 @@
+// 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/Ultrasonic.h"
+#include "frc/simulation/UltrasonicSim.h"
+#include "gtest/gtest.h"
+
+namespace frc {
+
+TEST(UltrasonicTest, SimDevices) {
+  Ultrasonic ultrasonic{0, 1};
+  sim::UltrasonicSim sim{ultrasonic};
+
+  EXPECT_EQ(0, ultrasonic.GetRange().value());
+  EXPECT_TRUE(ultrasonic.IsRangeValid());
+
+  sim.SetRange(units::meter_t{35.04});
+  EXPECT_EQ(35.04, ultrasonic.GetRange().value());
+
+  sim.SetRangeValid(false);
+  EXPECT_FALSE(ultrasonic.IsRangeValid());
+  EXPECT_EQ(0, ultrasonic.GetRange().value());
+}
+
+}  // namespace frc
diff --git a/wpilibc/src/test/native/cpp/WatchdogTest.cpp b/wpilibc/src/test/native/cpp/WatchdogTest.cpp
index 0e33511..5f55c2f 100644
--- a/wpilibc/src/test/native/cpp/WatchdogTest.cpp
+++ b/wpilibc/src/test/native/cpp/WatchdogTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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/Watchdog.h"  // NOLINT(build/include_order)
 
@@ -78,7 +75,7 @@
   frc::sim::StepTiming(0.2_s);
   watchdog.SetTimeout(0.2_s);
 
-  EXPECT_EQ(0.2, watchdog.GetTimeout());
+  EXPECT_EQ(0.2_s, watchdog.GetTimeout());
   EXPECT_EQ(0u, watchdogCounter) << "Watchdog triggered early";
 
   frc::sim::StepTiming(0.3_s);
diff --git a/wpilibc/src/test/native/cpp/XboxControllerTest.cpp b/wpilibc/src/test/native/cpp/XboxControllerTest.cpp
index 66ccfcd..0a8316b 100644
--- a/wpilibc/src/test/native/cpp/XboxControllerTest.cpp
+++ b/wpilibc/src/test/native/cpp/XboxControllerTest.cpp
@@ -1,74 +1,32 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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/XboxController.h"  // NOLINT(build/include_order)
 
+#include "JoystickTestMacros.h"
 #include "frc/simulation/XboxControllerSim.h"
 #include "gtest/gtest.h"
 
 using namespace frc;
 
-TEST(XboxControllerTests, GetX) {
-  XboxController joy{2};
-  sim::XboxControllerSim joysim{joy};
+BUTTON_TEST(XboxController, LeftBumper)
+BUTTON_TEST(XboxController, RightBumper)
 
-  joysim.SetX(XboxController::kLeftHand, 0.35);
-  joysim.SetX(XboxController::kRightHand, 0.45);
-  joysim.NotifyNewData();
-  ASSERT_NEAR(joy.GetX(XboxController::kLeftHand), 0.35, 0.001);
-  ASSERT_NEAR(joy.GetX(XboxController::kRightHand), 0.45, 0.001);
-}
+BUTTON_TEST(XboxController, LeftStickButton)
+BUTTON_TEST(XboxController, RightStickButton)
 
-TEST(XboxControllerTests, GetBumper) {
-  XboxController joy{1};
-  sim::XboxControllerSim joysim{joy};
+BUTTON_TEST(XboxController, AButton)
+BUTTON_TEST(XboxController, BButton)
+BUTTON_TEST(XboxController, XButton)
+BUTTON_TEST(XboxController, YButton)
+BUTTON_TEST(XboxController, BackButton)
+BUTTON_TEST(XboxController, StartButton)
 
-  joysim.SetBumper(XboxController::kLeftHand, false);
-  joysim.SetBumper(XboxController::kRightHand, true);
-  joysim.NotifyNewData();
-  ASSERT_FALSE(joy.GetBumper(XboxController::kLeftHand));
-  ASSERT_TRUE(joy.GetBumper(XboxController::kRightHand));
-  // need to call pressed and released to clear flags
-  joy.GetBumperPressed(XboxController::kLeftHand);
-  joy.GetBumperReleased(XboxController::kLeftHand);
-  joy.GetBumperPressed(XboxController::kRightHand);
-  joy.GetBumperReleased(XboxController::kRightHand);
+AXIS_TEST(XboxController, LeftX)
+AXIS_TEST(XboxController, RightX)
+AXIS_TEST(XboxController, LeftY)
+AXIS_TEST(XboxController, RightY)
 
-  joysim.SetBumper(XboxController::kLeftHand, true);
-  joysim.SetBumper(XboxController::kRightHand, false);
-  joysim.NotifyNewData();
-  ASSERT_TRUE(joy.GetBumper(XboxController::kLeftHand));
-  ASSERT_TRUE(joy.GetBumperPressed(XboxController::kLeftHand));
-  ASSERT_FALSE(joy.GetBumperReleased(XboxController::kLeftHand));
-  ASSERT_FALSE(joy.GetBumper(XboxController::kRightHand));
-  ASSERT_FALSE(joy.GetBumperPressed(XboxController::kRightHand));
-  ASSERT_TRUE(joy.GetBumperReleased(XboxController::kRightHand));
-}
-
-TEST(XboxControllerTests, GetAButton) {
-  XboxController joy{1};
-  sim::XboxControllerSim joysim{joy};
-
-  joysim.SetAButton(false);
-  joysim.NotifyNewData();
-  ASSERT_FALSE(joy.GetAButton());
-  // need to call pressed and released to clear flags
-  joy.GetAButtonPressed();
-  joy.GetAButtonReleased();
-
-  joysim.SetAButton(true);
-  joysim.NotifyNewData();
-  ASSERT_TRUE(joy.GetAButton());
-  ASSERT_TRUE(joy.GetAButtonPressed());
-  ASSERT_FALSE(joy.GetAButtonReleased());
-
-  joysim.SetAButton(false);
-  joysim.NotifyNewData();
-  ASSERT_FALSE(joy.GetAButton());
-  ASSERT_FALSE(joy.GetAButtonPressed());
-  ASSERT_TRUE(joy.GetAButtonReleased());
-}
+AXIS_TEST(XboxController, LeftTriggerAxis)
+AXIS_TEST(XboxController, RightTriggerAxis)
diff --git a/wpilibc/src/test/native/cpp/controller/ControllerUtilTest.cpp b/wpilibc/src/test/native/cpp/controller/ControllerUtilTest.cpp
deleted file mode 100644
index 00259d1..0000000
--- a/wpilibc/src/test/native/cpp/controller/ControllerUtilTest.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include <units/angle.h>
-
-#include "frc/controller/ControllerUtil.h"
-#include "gtest/gtest.h"
-
-TEST(ControllerUtilTest, GetModulusError) {
-  // Test symmetric range
-  EXPECT_FLOAT_EQ(-20.0, frc::GetModulusError(170.0, -170.0, -180.0, 180.0));
-  EXPECT_FLOAT_EQ(-20.0,
-                  frc::GetModulusError(170.0 + 360.0, -170.0, -180.0, 180.0));
-  EXPECT_FLOAT_EQ(-20.0,
-                  frc::GetModulusError(170.0, -170.0 + 360.0, -180.0, 180.0));
-  EXPECT_FLOAT_EQ(20.0, frc::GetModulusError(-170.0, 170.0, -180.0, 180.0));
-  EXPECT_FLOAT_EQ(20.0,
-                  frc::GetModulusError(-170.0 + 360.0, 170.0, -180.0, 180.0));
-  EXPECT_FLOAT_EQ(20.0,
-                  frc::GetModulusError(-170.0, 170.0 + 360.0, -180.0, 180.0));
-
-  // Test range starting at zero
-  EXPECT_FLOAT_EQ(-20.0, frc::GetModulusError(170.0, 190.0, 0.0, 360.0));
-  EXPECT_FLOAT_EQ(-20.0,
-                  frc::GetModulusError(170.0 + 360.0, 190.0, 0.0, 360.0));
-  EXPECT_FLOAT_EQ(-20.0,
-                  frc::GetModulusError(170.0, 190.0 + 360.0, 0.0, 360.0));
-
-  // Test asymmetric range that doesn't start at zero
-  EXPECT_FLOAT_EQ(-20.0, frc::GetModulusError(170.0, -170.0, -170.0, 190.0));
-
-  // Test all supported types
-  EXPECT_FLOAT_EQ(-20.0,
-                  frc::GetModulusError<double>(170.0, -170.0, -170.0, 190.0));
-  EXPECT_EQ(-20, frc::GetModulusError<int>(170, -170, -170, 190));
-  EXPECT_EQ(-20_deg, frc::GetModulusError<units::degree_t>(170_deg, -170_deg,
-                                                           -170_deg, 190_deg));
-}
diff --git a/wpilibc/src/test/native/cpp/controller/HolonomicDriveControllerTest.cpp b/wpilibc/src/test/native/cpp/controller/HolonomicDriveControllerTest.cpp
deleted file mode 100644
index fb72a4a..0000000
--- a/wpilibc/src/test/native/cpp/controller/HolonomicDriveControllerTest.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include <units/math.h>
-#include <units/time.h>
-#include <wpi/math>
-
-#include "frc/controller/HolonomicDriveController.h"
-#include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
-
-#define EXPECT_NEAR_UNITS(val1, val2, eps) \
-  EXPECT_LE(units::math::abs(val1 - val2), eps)
-
-static constexpr units::meter_t kTolerance{1 / 12.0};
-static constexpr units::radian_t kAngularTolerance{2.0 * wpi::math::pi / 180.0};
-
-TEST(HolonomicDriveControllerTest, ReachesReference) {
-  frc::HolonomicDriveController controller{
-      frc2::PIDController{1.0, 0.0, 0.0}, frc2::PIDController{1.0, 0.0, 0.0},
-      frc::ProfiledPIDController<units::radian>{
-          1.0, 0.0, 0.0,
-          frc::TrapezoidProfile<units::radian>::Constraints{
-              6.28_rad_per_s, 3.14_rad_per_s / 1_s}}};
-
-  frc::Pose2d robotPose{2.7_m, 23_m, frc::Rotation2d{0_deg}};
-
-  auto waypoints = std::vector{frc::Pose2d{2.75_m, 22.521_m, 0_rad},
-                               frc::Pose2d{24.73_m, 19.68_m, 5.846_rad}};
-  auto trajectory = frc::TrajectoryGenerator::GenerateTrajectory(
-      waypoints, {8.0_mps, 4.0_mps_sq});
-
-  constexpr auto kDt = 0.02_s;
-  auto totalTime = trajectory.TotalTime();
-  for (size_t i = 0; i < (totalTime / kDt).to<double>(); ++i) {
-    auto state = trajectory.Sample(kDt * i);
-    auto [vx, vy, omega] = controller.Calculate(robotPose, state, 0_rad);
-
-    robotPose = robotPose.Exp(frc::Twist2d{vx * kDt, vy * kDt, omega * kDt});
-  }
-
-  auto& endPose = trajectory.States().back().pose;
-  EXPECT_NEAR_UNITS(endPose.X(), robotPose.X(), kTolerance);
-  EXPECT_NEAR_UNITS(endPose.Y(), robotPose.Y(), kTolerance);
-  EXPECT_NEAR_UNITS(units::math::NormalizeAngle(robotPose.Rotation().Radians()),
-                    0_rad, kAngularTolerance);
-}
diff --git a/wpilibc/src/test/native/cpp/controller/PIDInputOutputTest.cpp b/wpilibc/src/test/native/cpp/controller/PIDInputOutputTest.cpp
deleted file mode 100644
index 3580308..0000000
--- a/wpilibc/src/test/native/cpp/controller/PIDInputOutputTest.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2014-2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/controller/PIDController.h"
-#include "gtest/gtest.h"
-
-class PIDInputOutputTest : public testing::Test {
- protected:
-  frc2::PIDController* controller;
-
-  void SetUp() override { controller = new frc2::PIDController(0, 0, 0); }
-
-  void TearDown() override { delete controller; }
-};
-
-TEST_F(PIDInputOutputTest, ContinuousInputTest) {
-  controller->SetP(1);
-  controller->EnableContinuousInput(-180, 180);
-
-  EXPECT_LT(controller->Calculate(-179, 179), 0);
-}
-
-TEST_F(PIDInputOutputTest, ProportionalGainOutputTest) {
-  controller->SetP(4);
-
-  EXPECT_DOUBLE_EQ(-0.1, controller->Calculate(0.025, 0));
-}
-
-TEST_F(PIDInputOutputTest, IntegralGainOutputTest) {
-  controller->SetI(4);
-
-  double out = 0;
-
-  for (int i = 0; i < 5; i++) {
-    out = controller->Calculate(0.025, 0);
-  }
-
-  EXPECT_DOUBLE_EQ(-0.5 * controller->GetPeriod().to<double>(), out);
-}
-
-TEST_F(PIDInputOutputTest, DerivativeGainOutputTest) {
-  controller->SetD(4);
-
-  controller->Calculate(0, 0);
-
-  EXPECT_DOUBLE_EQ(-10_ms / controller->GetPeriod(),
-                   controller->Calculate(0.0025, 0));
-}
diff --git a/wpilibc/src/test/native/cpp/controller/PIDToleranceTest.cpp b/wpilibc/src/test/native/cpp/controller/PIDToleranceTest.cpp
deleted file mode 100644
index 3251098..0000000
--- a/wpilibc/src/test/native/cpp/controller/PIDToleranceTest.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/controller/PIDController.h"
-#include "gtest/gtest.h"
-
-static constexpr double kSetpoint = 50.0;
-static constexpr double kRange = 200;
-static constexpr double kTolerance = 10.0;
-
-TEST(PIDToleranceTest, InitialTolerance) {
-  frc2::PIDController controller{0.5, 0.0, 0.0};
-  controller.EnableContinuousInput(-kRange / 2, kRange / 2);
-
-  EXPECT_TRUE(controller.AtSetpoint());
-}
-
-TEST(PIDToleranceTest, AbsoluteTolerance) {
-  frc2::PIDController controller{0.5, 0.0, 0.0};
-  controller.EnableContinuousInput(-kRange / 2, kRange / 2);
-
-  controller.SetTolerance(kTolerance);
-  controller.SetSetpoint(kSetpoint);
-
-  controller.Calculate(0.0);
-
-  EXPECT_FALSE(controller.AtSetpoint())
-      << "Error was in tolerance when it should not have been. Error was "
-      << controller.GetPositionError();
-
-  controller.Calculate(kSetpoint + kTolerance / 2);
-
-  EXPECT_TRUE(controller.AtSetpoint())
-      << "Error was not in tolerance when it should have been. Error was "
-      << controller.GetPositionError();
-
-  controller.Calculate(kSetpoint + 10 * kTolerance);
-
-  EXPECT_FALSE(controller.AtSetpoint())
-      << "Error was in tolerance when it should not have been. Error was "
-      << controller.GetPositionError();
-}
diff --git a/wpilibc/src/test/native/cpp/controller/ProfiledPIDInputOutputTest.cpp b/wpilibc/src/test/native/cpp/controller/ProfiledPIDInputOutputTest.cpp
deleted file mode 100644
index 6f9c36d..0000000
--- a/wpilibc/src/test/native/cpp/controller/ProfiledPIDInputOutputTest.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include <units/angle.h>
-#include <units/angular_acceleration.h>
-#include <units/angular_velocity.h>
-#include <wpi/math>
-
-#include "frc/controller/ProfiledPIDController.h"
-#include "gtest/gtest.h"
-
-class ProfiledPIDInputOutputTest : public testing::Test {
- protected:
-  frc::ProfiledPIDController<units::degrees>* controller;
-
-  void SetUp() override {
-    controller = new frc::ProfiledPIDController<units::degrees>(
-        0, 0, 0, {360_deg_per_s, 180_deg_per_s_sq});
-  }
-
-  void TearDown() override { delete controller; }
-};
-
-TEST_F(ProfiledPIDInputOutputTest, ContinuousInputTest) {
-  controller->SetP(1);
-  controller->EnableContinuousInput(-180_deg, 180_deg);
-
-  controller->Reset(-179_deg);
-  EXPECT_LT(controller->Calculate(-179_deg, 179_deg), 0);
-}
-
-TEST_F(ProfiledPIDInputOutputTest, ContinuousInputTest1) {
-  controller->SetP(1);
-  controller->EnableContinuousInput(-180_deg, 180_deg);
-
-  static constexpr units::degree_t kSetpoint{-179.0};
-  static constexpr units::degree_t kMeasurement{-179.0};
-  static constexpr units::degree_t kGoal{179.0};
-
-  controller->Reset(kSetpoint);
-  EXPECT_LT(controller->Calculate(kMeasurement, kGoal), 0.0);
-
-  // Error must be less than half the input range at all times
-  EXPECT_LT(units::math::abs(controller->GetSetpoint().position - kMeasurement),
-            180_deg);
-}
-
-TEST_F(ProfiledPIDInputOutputTest, ContinuousInputTest2) {
-  controller->SetP(1);
-  controller->EnableContinuousInput(-units::radian_t{wpi::math::pi},
-                                    units::radian_t{wpi::math::pi});
-
-  static constexpr units::radian_t kSetpoint{-3.4826633343199735};
-  static constexpr units::radian_t kMeasurement{-3.1352207333939606};
-  static constexpr units::radian_t kGoal{-3.534162788601621};
-
-  controller->Reset(kSetpoint);
-  EXPECT_LT(controller->Calculate(kMeasurement, kGoal), 0.0);
-
-  // Error must be less than half the input range at all times
-  EXPECT_LT(units::math::abs(controller->GetSetpoint().position - kMeasurement),
-            units::radian_t{wpi::math::pi});
-}
-
-TEST_F(ProfiledPIDInputOutputTest, ContinuousInputTest3) {
-  controller->SetP(1);
-  controller->EnableContinuousInput(-units::radian_t{wpi::math::pi},
-                                    units::radian_t{wpi::math::pi});
-
-  static constexpr units::radian_t kSetpoint{-3.5176604690006377};
-  static constexpr units::radian_t kMeasurement{3.1191729343822456};
-  static constexpr units::radian_t kGoal{2.709680418117445};
-
-  controller->Reset(kSetpoint);
-  EXPECT_LT(controller->Calculate(kMeasurement, kGoal), 0.0);
-
-  // Error must be less than half the input range at all times
-  EXPECT_LT(units::math::abs(controller->GetSetpoint().position - kMeasurement),
-            units::radian_t{wpi::math::pi});
-}
-
-TEST_F(ProfiledPIDInputOutputTest, ProportionalGainOutputTest) {
-  controller->SetP(4);
-
-  EXPECT_DOUBLE_EQ(-0.1, controller->Calculate(0.025_deg, 0_deg));
-}
-
-TEST_F(ProfiledPIDInputOutputTest, IntegralGainOutputTest) {
-  controller->SetI(4);
-
-  double out = 0;
-
-  for (int i = 0; i < 5; i++) {
-    out = controller->Calculate(0.025_deg, 0_deg);
-  }
-
-  EXPECT_DOUBLE_EQ(-0.5 * controller->GetPeriod().to<double>(), out);
-}
-
-TEST_F(ProfiledPIDInputOutputTest, DerivativeGainOutputTest) {
-  controller->SetD(4);
-
-  controller->Calculate(0_deg, 0_deg);
-
-  EXPECT_DOUBLE_EQ(-10_ms / controller->GetPeriod(),
-                   controller->Calculate(0.0025_deg, 0_deg));
-}
diff --git a/wpilibc/src/test/native/cpp/controller/RamseteControllerTest.cpp b/wpilibc/src/test/native/cpp/controller/RamseteControllerTest.cpp
deleted file mode 100644
index fb34385..0000000
--- a/wpilibc/src/test/native/cpp/controller/RamseteControllerTest.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include <units/math.h>
-
-#include "frc/controller/RamseteController.h"
-#include "frc/trajectory/TrajectoryGenerator.h"
-#include "gtest/gtest.h"
-
-#define EXPECT_NEAR_UNITS(val1, val2, eps) \
-  EXPECT_LE(units::math::abs(val1 - val2), eps)
-
-static constexpr units::meter_t kTolerance{1 / 12.0};
-static constexpr units::radian_t kAngularTolerance{2.0 * wpi::math::pi / 180.0};
-
-TEST(RamseteControllerTest, ReachesReference) {
-  frc::RamseteController controller{2.0, 0.7};
-  frc::Pose2d robotPose{2.7_m, 23_m, frc::Rotation2d{0_deg}};
-
-  auto waypoints = std::vector{frc::Pose2d{2.75_m, 22.521_m, 0_rad},
-                               frc::Pose2d{24.73_m, 19.68_m, 5.846_rad}};
-  auto trajectory = frc::TrajectoryGenerator::GenerateTrajectory(
-      waypoints, {8.8_mps, 0.1_mps_sq});
-
-  constexpr auto kDt = 0.02_s;
-  auto totalTime = trajectory.TotalTime();
-  for (size_t i = 0; i < (totalTime / kDt).to<double>(); ++i) {
-    auto state = trajectory.Sample(kDt * i);
-    auto [vx, vy, omega] = controller.Calculate(robotPose, state);
-    static_cast<void>(vy);
-
-    robotPose = robotPose.Exp(frc::Twist2d{vx * kDt, 0_m, omega * kDt});
-  }
-
-  auto& endPose = trajectory.States().back().pose;
-  EXPECT_NEAR_UNITS(endPose.X(), robotPose.X(), kTolerance);
-  EXPECT_NEAR_UNITS(endPose.Y(), robotPose.Y(), kTolerance);
-  EXPECT_NEAR_UNITS(units::math::NormalizeAngle(endPose.Rotation().Radians() -
-                                                robotPose.Rotation().Radians()),
-                    0_rad, kAngularTolerance);
-}
diff --git a/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp b/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp
new file mode 100644
index 0000000..1d13a77
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/drive/DifferentialDriveTest.cpp
@@ -0,0 +1,229 @@
+// 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 "MockMotorController.h"
+#include "frc/drive/DifferentialDrive.h"
+#include "gtest/gtest.h"
+
+TEST(DifferentialDriveTest, ArcadeDrive) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::DifferentialDrive drive{left, right};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.ArcadeDrive(1.0, 0.0, false);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward left turn
+  drive.ArcadeDrive(0.5, -0.5, false);
+  EXPECT_DOUBLE_EQ(0.0, left.Get());
+  EXPECT_DOUBLE_EQ(0.5, right.Get());
+
+  // Forward right turn
+  drive.ArcadeDrive(0.5, 0.5, false);
+  EXPECT_DOUBLE_EQ(0.5, left.Get());
+  EXPECT_DOUBLE_EQ(0.0, right.Get());
+
+  // Backward
+  drive.ArcadeDrive(-1.0, 0.0, false);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+
+  // Backward left turn
+  drive.ArcadeDrive(-0.5, -0.5, false);
+  EXPECT_DOUBLE_EQ(-0.5, left.Get());
+  EXPECT_DOUBLE_EQ(0.0, right.Get());
+
+  // Backward right turn
+  drive.ArcadeDrive(-0.5, 0.5, false);
+  EXPECT_DOUBLE_EQ(0.0, left.Get());
+  EXPECT_DOUBLE_EQ(-0.5, right.Get());
+}
+
+TEST(DifferentialDriveTest, ArcadeDriveSquared) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::DifferentialDrive drive{left, right};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.ArcadeDrive(1.0, 0.0, true);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward left turn
+  drive.ArcadeDrive(0.5, -0.5, true);
+  EXPECT_DOUBLE_EQ(0.0, left.Get());
+  EXPECT_DOUBLE_EQ(0.25, right.Get());
+
+  // Forward right turn
+  drive.ArcadeDrive(0.5, 0.5, true);
+  EXPECT_DOUBLE_EQ(0.25, left.Get());
+  EXPECT_DOUBLE_EQ(0.0, right.Get());
+
+  // Backward
+  drive.ArcadeDrive(-1.0, 0.0, true);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+
+  // Backward left turn
+  drive.ArcadeDrive(-0.5, -0.5, true);
+  EXPECT_DOUBLE_EQ(-0.25, left.Get());
+  EXPECT_DOUBLE_EQ(0.0, right.Get());
+
+  // Backward right turn
+  drive.ArcadeDrive(-0.5, 0.5, true);
+  EXPECT_DOUBLE_EQ(0.0, left.Get());
+  EXPECT_DOUBLE_EQ(-0.25, right.Get());
+}
+
+TEST(DifferentialDriveTest, CurvatureDrive) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::DifferentialDrive drive{left, right};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.CurvatureDrive(1.0, 0.0, false);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward left turn
+  drive.CurvatureDrive(0.5, -0.5, false);
+  EXPECT_DOUBLE_EQ(0.25, left.Get());
+  EXPECT_DOUBLE_EQ(0.75, right.Get());
+
+  // Forward right turn
+  drive.CurvatureDrive(0.5, 0.5, false);
+  EXPECT_DOUBLE_EQ(0.75, left.Get());
+  EXPECT_DOUBLE_EQ(0.25, right.Get());
+
+  // Backward
+  drive.CurvatureDrive(-1.0, 0.0, false);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+
+  // Backward left turn
+  drive.CurvatureDrive(-0.5, -0.5, false);
+  EXPECT_DOUBLE_EQ(-0.75, left.Get());
+  EXPECT_DOUBLE_EQ(-0.25, right.Get());
+
+  // Backward right turn
+  drive.CurvatureDrive(-0.5, 0.5, false);
+  EXPECT_DOUBLE_EQ(-0.25, left.Get());
+  EXPECT_DOUBLE_EQ(-0.75, right.Get());
+}
+
+TEST(DifferentialDriveTest, CurvatureDriveTurnInPlace) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::DifferentialDrive drive{left, right};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.CurvatureDrive(1.0, 0.0, true);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward left turn
+  drive.CurvatureDrive(0.5, -0.5, true);
+  EXPECT_DOUBLE_EQ(0.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward right turn
+  drive.CurvatureDrive(0.5, 0.5, true);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(0.0, right.Get());
+
+  // Backward
+  drive.CurvatureDrive(-1.0, 0.0, true);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+
+  // Backward left turn
+  drive.CurvatureDrive(-0.5, -0.5, true);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(0.0, right.Get());
+
+  // Backward right turn
+  drive.CurvatureDrive(-0.5, 0.5, true);
+  EXPECT_DOUBLE_EQ(0.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+}
+
+TEST(DifferentialDriveTest, TankDrive) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::DifferentialDrive drive{left, right};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.TankDrive(1.0, 1.0, false);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward left turn
+  drive.TankDrive(0.5, 1.0, false);
+  EXPECT_DOUBLE_EQ(0.5, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward right turn
+  drive.TankDrive(1.0, 0.5, false);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(0.5, right.Get());
+
+  // Backward
+  drive.TankDrive(-1.0, -1.0, false);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+
+  // Backward left turn
+  drive.TankDrive(-0.5, -1.0, false);
+  EXPECT_DOUBLE_EQ(-0.5, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+
+  // Backward right turn
+  drive.TankDrive(-0.5, 1.0, false);
+  EXPECT_DOUBLE_EQ(-0.5, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+}
+
+TEST(DifferentialDriveTest, TankDriveSquared) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::DifferentialDrive drive{left, right};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.TankDrive(1.0, 1.0, true);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward left turn
+  drive.TankDrive(0.5, 1.0, true);
+  EXPECT_DOUBLE_EQ(0.25, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+
+  // Forward right turn
+  drive.TankDrive(1.0, 0.5, true);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(0.25, right.Get());
+
+  // Backward
+  drive.TankDrive(-1.0, -1.0, true);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+
+  // Backward left turn
+  drive.TankDrive(-0.5, -1.0, true);
+  EXPECT_DOUBLE_EQ(-0.25, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+
+  // Backward right turn
+  drive.TankDrive(-1.0, -0.5, true);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-0.25, right.Get());
+}
diff --git a/wpilibc/src/test/native/cpp/drive/DriveTest.cpp b/wpilibc/src/test/native/cpp/drive/DriveTest.cpp
deleted file mode 100644
index 1ebb6b3..0000000
--- a/wpilibc/src/test/native/cpp/drive/DriveTest.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* 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.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "MockSpeedController.h"
-#include "frc/RobotDrive.h"
-#include "frc/drive/DifferentialDrive.h"
-#include "frc/drive/MecanumDrive.h"
-#include "gtest/gtest.h"
-
-using namespace frc;
-
-class DriveTest : public testing::Test {
- protected:
-  MockSpeedController m_rdFrontLeft;
-  MockSpeedController m_rdRearLeft;
-  MockSpeedController m_rdFrontRight;
-  MockSpeedController m_rdRearRight;
-  MockSpeedController m_frontLeft;
-  MockSpeedController m_rearLeft;
-  MockSpeedController m_frontRight;
-  MockSpeedController m_rearRight;
-  frc::RobotDrive m_robotDrive{m_rdFrontLeft, m_rdRearLeft, m_rdFrontRight,
-                               m_rdRearRight};
-  frc::DifferentialDrive m_differentialDrive{m_frontLeft, m_frontRight};
-  frc::MecanumDrive m_mecanumDrive{m_frontLeft, m_rearLeft, m_frontRight,
-                                   m_rearRight};
-
-  double m_testJoystickValues[9] = {-1.0, -0.9, -0.5, -0.01, 0.0,
-                                    0.01, 0.5,  0.9,  1.0};
-  double m_testGyroValues[19] = {0,    45,   90,   135,  180, 225,  270,
-                                 305,  360,  540,  -45,  -90, -135, -180,
-                                 -225, -270, -305, -360, -540};
-};
-
-TEST_F(DriveTest, TankDrive) {
-  int joystickSize = sizeof(m_testJoystickValues) / sizeof(double);
-  double leftJoystick, rightJoystick;
-  m_differentialDrive.SetDeadband(0.0);
-  m_differentialDrive.SetSafetyEnabled(false);
-  m_mecanumDrive.SetSafetyEnabled(false);
-  m_robotDrive.SetSafetyEnabled(false);
-  for (int i = 0; i < joystickSize; i++) {
-    for (int j = 0; j < joystickSize; j++) {
-      leftJoystick = m_testJoystickValues[i];
-      rightJoystick = m_testJoystickValues[j];
-      m_robotDrive.TankDrive(leftJoystick, rightJoystick, false);
-      m_differentialDrive.TankDrive(leftJoystick, rightJoystick, false);
-      ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01);
-      ASSERT_NEAR(m_rdFrontRight.Get(), m_frontRight.Get(), 0.01);
-    }
-  }
-}
-
-TEST_F(DriveTest, TankDriveSquared) {
-  int joystickSize = sizeof(m_testJoystickValues) / sizeof(double);
-  double leftJoystick, rightJoystick;
-  m_differentialDrive.SetDeadband(0.0);
-  m_differentialDrive.SetSafetyEnabled(false);
-  m_mecanumDrive.SetSafetyEnabled(false);
-  m_robotDrive.SetSafetyEnabled(false);
-  for (int i = 0; i < joystickSize; i++) {
-    for (int j = 0; j < joystickSize; j++) {
-      leftJoystick = m_testJoystickValues[i];
-      rightJoystick = m_testJoystickValues[j];
-      m_robotDrive.TankDrive(leftJoystick, rightJoystick, true);
-      m_differentialDrive.TankDrive(leftJoystick, rightJoystick, true);
-      ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01);
-      ASSERT_NEAR(m_rdFrontRight.Get(), m_frontRight.Get(), 0.01);
-    }
-  }
-}
-
-TEST_F(DriveTest, ArcadeDriveSquared) {
-  int joystickSize = sizeof(m_testJoystickValues) / sizeof(double);
-  double moveJoystick, rotateJoystick;
-  m_differentialDrive.SetDeadband(0.0);
-  m_differentialDrive.SetSafetyEnabled(false);
-  m_mecanumDrive.SetSafetyEnabled(false);
-  m_robotDrive.SetSafetyEnabled(false);
-  for (int i = 0; i < joystickSize; i++) {
-    for (int j = 0; j < joystickSize; j++) {
-      moveJoystick = m_testJoystickValues[i];
-      rotateJoystick = m_testJoystickValues[j];
-      m_robotDrive.ArcadeDrive(moveJoystick, rotateJoystick, true);
-      m_differentialDrive.ArcadeDrive(moveJoystick, -rotateJoystick, true);
-      ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01);
-      ASSERT_NEAR(m_rdFrontRight.Get(), m_frontRight.Get(), 0.01);
-    }
-  }
-}
-
-TEST_F(DriveTest, ArcadeDrive) {
-  int joystickSize = sizeof(m_testJoystickValues) / sizeof(double);
-  double moveJoystick, rotateJoystick;
-  m_differentialDrive.SetDeadband(0.0);
-  m_differentialDrive.SetSafetyEnabled(false);
-  m_mecanumDrive.SetSafetyEnabled(false);
-  m_robotDrive.SetSafetyEnabled(false);
-  for (int i = 0; i < joystickSize; i++) {
-    for (int j = 0; j < joystickSize; j++) {
-      moveJoystick = m_testJoystickValues[i];
-      rotateJoystick = m_testJoystickValues[j];
-      m_robotDrive.ArcadeDrive(moveJoystick, rotateJoystick, false);
-      m_differentialDrive.ArcadeDrive(moveJoystick, -rotateJoystick, false);
-      ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01);
-      ASSERT_NEAR(m_rdFrontRight.Get(), m_frontRight.Get(), 0.01);
-    }
-  }
-}
-
-TEST_F(DriveTest, MecanumCartesian) {
-  int joystickSize = sizeof(m_testJoystickValues) / sizeof(double);
-  int gyroSize = sizeof(m_testGyroValues) / sizeof(double);
-  double xJoystick, yJoystick, rotateJoystick, gyroValue;
-  m_mecanumDrive.SetDeadband(0.0);
-  m_mecanumDrive.SetSafetyEnabled(false);
-  m_differentialDrive.SetSafetyEnabled(false);
-  m_robotDrive.SetSafetyEnabled(false);
-  for (int i = 0; i < joystickSize; i++) {
-    for (int j = 0; j < joystickSize; j++) {
-      for (int k = 0; k < joystickSize; k++) {
-        for (int l = 0; l < gyroSize; l++) {
-          xJoystick = m_testJoystickValues[i];
-          yJoystick = m_testJoystickValues[j];
-          rotateJoystick = m_testJoystickValues[k];
-          gyroValue = m_testGyroValues[l];
-          m_robotDrive.MecanumDrive_Cartesian(xJoystick, yJoystick,
-                                              rotateJoystick, gyroValue);
-          m_mecanumDrive.DriveCartesian(xJoystick, -yJoystick, rotateJoystick,
-                                        -gyroValue);
-          ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01)
-              << "X: " << xJoystick << " Y: " << yJoystick
-              << " Rotate: " << rotateJoystick << " Gyro: " << gyroValue;
-          ASSERT_NEAR(m_rdFrontRight.Get(), -m_frontRight.Get(), 0.01)
-              << "X: " << xJoystick << " Y: " << yJoystick
-              << " Rotate: " << rotateJoystick << " Gyro: " << gyroValue;
-          ASSERT_NEAR(m_rdRearLeft.Get(), m_rearLeft.Get(), 0.01)
-              << "X: " << xJoystick << " Y: " << yJoystick
-              << " Rotate: " << rotateJoystick << " Gyro: " << gyroValue;
-          ASSERT_NEAR(m_rdRearRight.Get(), -m_rearRight.Get(), 0.01)
-              << "X: " << xJoystick << " Y: " << yJoystick
-              << " Rotate: " << rotateJoystick << " Gyro: " << gyroValue;
-        }
-      }
-    }
-  }
-}
-
-TEST_F(DriveTest, MecanumPolar) {
-  int joystickSize = sizeof(m_testJoystickValues) / sizeof(double);
-  int gyroSize = sizeof(m_testGyroValues) / sizeof(double);
-  double magnitudeJoystick, directionJoystick, rotateJoystick;
-  m_mecanumDrive.SetDeadband(0.0);
-  m_mecanumDrive.SetSafetyEnabled(false);
-  m_differentialDrive.SetSafetyEnabled(false);
-  m_robotDrive.SetSafetyEnabled(false);
-  for (int i = 0; i < joystickSize; i++) {
-    for (int j = 0; j < gyroSize; j++) {
-      for (int k = 0; k < joystickSize; k++) {
-        magnitudeJoystick = m_testJoystickValues[i];
-        directionJoystick = m_testGyroValues[j];
-        rotateJoystick = m_testJoystickValues[k];
-        m_robotDrive.MecanumDrive_Polar(magnitudeJoystick, directionJoystick,
-                                        rotateJoystick);
-        m_mecanumDrive.DrivePolar(magnitudeJoystick, directionJoystick,
-                                  rotateJoystick);
-        ASSERT_NEAR(m_rdFrontLeft.Get(), m_frontLeft.Get(), 0.01)
-            << "Magnitude: " << magnitudeJoystick
-            << " Direction: " << directionJoystick
-            << " Rotate: " << rotateJoystick;
-        ASSERT_NEAR(m_rdFrontRight.Get(), -m_frontRight.Get(), 0.01)
-            << "Magnitude: " << magnitudeJoystick
-            << " Direction: " << directionJoystick
-            << " Rotate: " << rotateJoystick;
-        ASSERT_NEAR(m_rdRearLeft.Get(), m_rearLeft.Get(), 0.01)
-            << "Magnitude: " << magnitudeJoystick
-            << " Direction: " << directionJoystick
-            << " Rotate: " << rotateJoystick;
-        ASSERT_NEAR(m_rdRearRight.Get(), -m_rearRight.Get(), 0.01)
-            << "Magnitude: " << magnitudeJoystick
-            << " Direction: " << directionJoystick
-            << " Rotate: " << rotateJoystick;
-      }
-    }
-  }
-}
diff --git a/wpilibc/src/test/native/cpp/drive/KilloughDriveTest.cpp b/wpilibc/src/test/native/cpp/drive/KilloughDriveTest.cpp
new file mode 100644
index 0000000..510b377
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/drive/KilloughDriveTest.cpp
@@ -0,0 +1,123 @@
+// 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 "MockMotorController.h"
+#include "frc/drive/KilloughDrive.h"
+#include "gtest/gtest.h"
+
+TEST(KilloughDriveTest, Cartesian) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::MockMotorController back;
+  frc::KilloughDrive drive{left, right, back};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.DriveCartesian(1.0, 0.0, 0.0);
+  EXPECT_DOUBLE_EQ(0.5, left.Get());
+  EXPECT_DOUBLE_EQ(-0.5, right.Get());
+  EXPECT_NEAR(0.0, back.Get(), 1e-9);
+
+  // Left
+  drive.DriveCartesian(0.0, -1.0, 0.0);
+  EXPECT_DOUBLE_EQ(-std::sqrt(3) / 2, left.Get());
+  EXPECT_DOUBLE_EQ(-std::sqrt(3) / 2, right.Get());
+  EXPECT_DOUBLE_EQ(1.0, back.Get());
+
+  // Right
+  drive.DriveCartesian(0.0, 1.0, 0.0);
+  EXPECT_DOUBLE_EQ(std::sqrt(3) / 2, left.Get());
+  EXPECT_DOUBLE_EQ(std::sqrt(3) / 2, right.Get());
+  EXPECT_DOUBLE_EQ(-1.0, back.Get());
+
+  // Rotate CCW
+  drive.DriveCartesian(0.0, 0.0, -1.0);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+  EXPECT_DOUBLE_EQ(-1.0, back.Get());
+
+  // Rotate CW
+  drive.DriveCartesian(0.0, 0.0, 1.0);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+  EXPECT_DOUBLE_EQ(1.0, back.Get());
+}
+
+TEST(KilloughDriveTest, CartesianGyro90CW) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::MockMotorController back;
+  frc::KilloughDrive drive{left, right, back};
+  drive.SetDeadband(0.0);
+
+  // Forward in global frame; left in robot frame
+  drive.DriveCartesian(1.0, 0.0, 0.0, 90.0);
+  EXPECT_DOUBLE_EQ(-std::sqrt(3) / 2, left.Get());
+  EXPECT_DOUBLE_EQ(-std::sqrt(3) / 2, right.Get());
+  EXPECT_DOUBLE_EQ(1.0, back.Get());
+
+  // Left in global frame; backward in robot frame
+  drive.DriveCartesian(0.0, -1.0, 0.0, 90.0);
+  EXPECT_DOUBLE_EQ(-0.5, left.Get());
+  EXPECT_NEAR(0.5, right.Get(), 1e-9);
+  EXPECT_NEAR(0.0, back.Get(), 1e-9);
+
+  // Right in global frame; forward in robot frame
+  drive.DriveCartesian(0.0, 1.0, 0.0, 90.0);
+  EXPECT_DOUBLE_EQ(0.5, left.Get());
+  EXPECT_NEAR(-0.5, right.Get(), 1e-9);
+  EXPECT_NEAR(0.0, back.Get(), 1e-9);
+
+  // Rotate CCW
+  drive.DriveCartesian(0.0, 0.0, -1.0, 90.0);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+  EXPECT_DOUBLE_EQ(-1.0, back.Get());
+
+  // Rotate CW
+  drive.DriveCartesian(0.0, 0.0, 1.0, 90.0);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+  EXPECT_DOUBLE_EQ(1.0, back.Get());
+}
+
+TEST(KilloughDriveTest, Polar) {
+  frc::MockMotorController left;
+  frc::MockMotorController right;
+  frc::MockMotorController back;
+  frc::KilloughDrive drive{left, right, back};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.DrivePolar(1.0, 0.0, 0.0);
+  EXPECT_DOUBLE_EQ(std::sqrt(3) / 2, left.Get());
+  EXPECT_DOUBLE_EQ(std::sqrt(3) / 2, right.Get());
+  EXPECT_DOUBLE_EQ(-1.0, back.Get());
+
+  // Left
+  drive.DrivePolar(1.0, -90.0, 0.0);
+  EXPECT_DOUBLE_EQ(-0.5, left.Get());
+  EXPECT_DOUBLE_EQ(0.5, right.Get());
+  EXPECT_NEAR(0.0, back.Get(), 1e-9);
+
+  // Right
+  drive.DrivePolar(1.0, 90.0, 0.0);
+  EXPECT_DOUBLE_EQ(0.5, left.Get());
+  EXPECT_NEAR(-0.5, right.Get(), 1e-9);
+  EXPECT_NEAR(0.0, back.Get(), 1e-9);
+
+  // Rotate CCW
+  drive.DrivePolar(0.0, 0.0, -1.0);
+  EXPECT_DOUBLE_EQ(-1.0, left.Get());
+  EXPECT_DOUBLE_EQ(-1.0, right.Get());
+  EXPECT_DOUBLE_EQ(-1.0, back.Get());
+
+  // Rotate CW
+  drive.DrivePolar(0.0, 0.0, 1.0);
+  EXPECT_DOUBLE_EQ(1.0, left.Get());
+  EXPECT_DOUBLE_EQ(1.0, right.Get());
+  EXPECT_DOUBLE_EQ(1.0, back.Get());
+}
diff --git a/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp b/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp
new file mode 100644
index 0000000..fe0a73b
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/drive/MecanumDriveTest.cpp
@@ -0,0 +1,139 @@
+// 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 "MockMotorController.h"
+#include "frc/drive/MecanumDrive.h"
+#include "gtest/gtest.h"
+
+TEST(MecanumDriveTest, Cartesian) {
+  frc::MockMotorController fl;
+  frc::MockMotorController fr;
+  frc::MockMotorController rl;
+  frc::MockMotorController rr;
+  frc::MecanumDrive drive{fl, fr, rl, rr};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.DriveCartesian(1.0, 0.0, 0.0);
+  EXPECT_DOUBLE_EQ(1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(1.0, rr.Get());
+
+  // Left
+  drive.DriveCartesian(0.0, -1.0, 0.0);
+  EXPECT_DOUBLE_EQ(-1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rr.Get());
+
+  // Right
+  drive.DriveCartesian(0.0, 1.0, 0.0);
+  EXPECT_DOUBLE_EQ(1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(1.0, rr.Get());
+
+  // Rotate CCW
+  drive.DriveCartesian(0.0, 0.0, -1.0);
+  EXPECT_DOUBLE_EQ(-1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(1.0, rr.Get());
+
+  // Rotate CW
+  drive.DriveCartesian(0.0, 0.0, 1.0);
+  EXPECT_DOUBLE_EQ(1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rr.Get());
+}
+
+TEST(MecanumDriveTest, CartesianGyro90CW) {
+  frc::MockMotorController fl;
+  frc::MockMotorController fr;
+  frc::MockMotorController rl;
+  frc::MockMotorController rr;
+  frc::MecanumDrive drive{fl, fr, rl, rr};
+  drive.SetDeadband(0.0);
+
+  // Forward in global frame; left in robot frame
+  drive.DriveCartesian(1.0, 0.0, 0.0, 90.0);
+  EXPECT_DOUBLE_EQ(-1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rr.Get());
+
+  // Left in global frame; backward in robot frame
+  drive.DriveCartesian(0.0, -1.0, 0.0, 90.0);
+  EXPECT_DOUBLE_EQ(-1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rr.Get());
+
+  // Right in global frame; forward in robot frame
+  drive.DriveCartesian(0.0, 1.0, 0.0, 90.0);
+  EXPECT_DOUBLE_EQ(1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(1.0, rr.Get());
+
+  // Rotate CCW
+  drive.DriveCartesian(0.0, 0.0, -1.0, 90.0);
+  EXPECT_DOUBLE_EQ(-1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(1.0, rr.Get());
+
+  // Rotate CW
+  drive.DriveCartesian(0.0, 0.0, 1.0, 90.0);
+  EXPECT_DOUBLE_EQ(1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rr.Get());
+}
+
+TEST(MecanumDriveTest, Polar) {
+  frc::MockMotorController fl;
+  frc::MockMotorController fr;
+  frc::MockMotorController rl;
+  frc::MockMotorController rr;
+  frc::MecanumDrive drive{fl, fr, rl, rr};
+  drive.SetDeadband(0.0);
+
+  // Forward
+  drive.DrivePolar(1.0, 0.0, 0.0);
+  EXPECT_DOUBLE_EQ(1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(1.0, rr.Get());
+
+  // Left
+  drive.DrivePolar(1.0, -90.0, 0.0);
+  EXPECT_DOUBLE_EQ(-1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rr.Get());
+
+  // Right
+  drive.DrivePolar(1.0, 90.0, 0.0);
+  EXPECT_DOUBLE_EQ(1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(1.0, rr.Get());
+
+  // Rotate CCW
+  drive.DrivePolar(0.0, 0.0, -1.0);
+  EXPECT_DOUBLE_EQ(-1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(1.0, rr.Get());
+
+  // Rotate CW
+  drive.DrivePolar(0.0, 0.0, 1.0);
+  EXPECT_DOUBLE_EQ(1.0, fl.Get());
+  EXPECT_DOUBLE_EQ(1.0, fr.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rl.Get());
+  EXPECT_DOUBLE_EQ(-1.0, rr.Get());
+}
diff --git a/wpilibc/src/test/native/cpp/main.cpp b/wpilibc/src/test/native/cpp/main.cpp
index c6b6c58..6aea19a 100644
--- a/wpilibc/src/test/native/cpp/main.cpp
+++ b/wpilibc/src/test/native/cpp/main.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <hal/HALBase.h>
 
diff --git a/wpilibc/src/test/native/cpp/shuffleboard/MockActuatorSendable.cpp b/wpilibc/src/test/native/cpp/shuffleboard/MockActuatorSendable.cpp
index 172d7c6..5a9f993 100644
--- a/wpilibc/src/test/native/cpp/shuffleboard/MockActuatorSendable.cpp
+++ b/wpilibc/src/test/native/cpp/shuffleboard/MockActuatorSendable.cpp
@@ -1,20 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 "shuffleboard/MockActuatorSendable.h"
 
-#include "frc/smartdashboard/SendableRegistry.h"
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-using namespace frc;
-
-MockActuatorSendable::MockActuatorSendable(wpi::StringRef name) {
-  SendableRegistry::GetInstance().Add(this, name);
+MockActuatorSendable::MockActuatorSendable(std::string_view name) {
+  wpi::SendableRegistry::Add(this, name);
 }
 
-void MockActuatorSendable::InitSendable(SendableBuilder& builder) {
+void MockActuatorSendable::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetActuator(true);
 }
diff --git a/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardInstanceTest.cpp b/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardInstanceTest.cpp
index ae21526..d370a2d 100644
--- a/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardInstanceTest.cpp
+++ b/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardInstanceTest.cpp
@@ -1,50 +1,46 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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/shuffleboard/ShuffleboardInstance.h"  // NOLINT(build/include_order)
 
-#include <memory>
-#include <string>
+#include <string_view>
 
-#include <networktables/NetworkTableEntry.h>
 #include <networktables/NetworkTableInstance.h>
 
 #include "frc/shuffleboard/ShuffleboardInstance.h"
 #include "gtest/gtest.h"
 #include "shuffleboard/MockActuatorSendable.h"
 
-using namespace frc;
+class NTWrapper {
+ public:
+  NTWrapper() { inst = nt::NetworkTableInstance::Create(); }
 
-class ShuffleboardInstanceTest : public testing::Test {
-  void SetUp() override {
-    m_ntInstance = nt::NetworkTableInstance::Create();
-    m_shuffleboardInstance =
-        std::make_unique<detail::ShuffleboardInstance>(m_ntInstance);
-  }
+  ~NTWrapper() { nt::NetworkTableInstance::Destroy(inst); }
 
- protected:
-  nt::NetworkTableInstance m_ntInstance;
-  std::unique_ptr<detail::ShuffleboardInstance> m_shuffleboardInstance;
+  nt::NetworkTableInstance inst;
 };
 
-TEST_F(ShuffleboardInstanceTest, PathFluent) {
-  auto entry = m_shuffleboardInstance->GetTab("Tab Title")
-                   .GetLayout("List Layout", "List")
+TEST(ShuffleboardInstanceTest, PathFluent) {
+  NTWrapper ntInst;
+  frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
+
+  auto entry = shuffleboardInst.GetTab("Tab Title")
+                   .GetLayout("List", "List Layout")
                    .Add("Data", "string")
                    .WithWidget("Text View")
                    .GetEntry();
 
   EXPECT_EQ("string", entry.GetString("")) << "Wrong entry value";
-  EXPECT_EQ("/Shuffleboard/Tab Title/List Layout/Data", entry.GetName())
+  EXPECT_EQ("/Shuffleboard/Tab Title/List/Data", entry.GetName())
       << "Entry path generated incorrectly";
 }
 
-TEST_F(ShuffleboardInstanceTest, NestedLayoutsFluent) {
-  auto entry = m_shuffleboardInstance->GetTab("Tab")
+TEST(ShuffleboardInstanceTest, NestedLayoutsFluent) {
+  NTWrapper ntInst;
+  frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
+
+  auto entry = shuffleboardInst.GetTab("Tab")
                    .GetLayout("First", "List")
                    .GetLayout("Second", "List")
                    .GetLayout("Third", "List")
@@ -58,13 +54,16 @@
       << "Entry path generated incorrectly";
 }
 
-TEST_F(ShuffleboardInstanceTest, NestedLayoutsOop) {
-  ShuffleboardTab& tab = m_shuffleboardInstance->GetTab("Tab");
-  ShuffleboardLayout& first = tab.GetLayout("First", "List");
-  ShuffleboardLayout& second = first.GetLayout("Second", "List");
-  ShuffleboardLayout& third = second.GetLayout("Third", "List");
-  ShuffleboardLayout& fourth = third.GetLayout("Fourth", "List");
-  SimpleWidget& widget = fourth.Add("Value", "string");
+TEST(ShuffleboardInstanceTest, NestedLayoutsOop) {
+  NTWrapper ntInst;
+  frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
+
+  frc::ShuffleboardTab& tab = shuffleboardInst.GetTab("Tab");
+  frc::ShuffleboardLayout& first = tab.GetLayout("First", "List");
+  frc::ShuffleboardLayout& second = first.GetLayout("Second", "List");
+  frc::ShuffleboardLayout& third = second.GetLayout("Third", "List");
+  frc::ShuffleboardLayout& fourth = third.GetLayout("Fourth", "List");
+  frc::SimpleWidget& widget = fourth.Add("Value", "string");
   auto entry = widget.GetEntry();
 
   EXPECT_EQ("string", entry.GetString("")) << "Wrong entry value";
@@ -73,23 +72,27 @@
       << "Entry path generated incorrectly";
 }
 
-TEST_F(ShuffleboardInstanceTest, LayoutTypeIsSet) {
-  std::string layoutType = "Type";
-  m_shuffleboardInstance->GetTab("Tab").GetLayout("Title", layoutType);
-  m_shuffleboardInstance->Update();
-  nt::NetworkTableEntry entry = m_ntInstance.GetEntry(
+TEST(ShuffleboardInstanceTest, LayoutTypeIsSet) {
+  NTWrapper ntInst;
+  frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
+
+  std::string_view layoutType = "Type";
+  shuffleboardInst.GetTab("Tab").GetLayout("Title", layoutType);
+  shuffleboardInst.Update();
+  auto entry = ntInst.inst.GetEntry(
       "/Shuffleboard/.metadata/Tab/Title/PreferredComponent");
   EXPECT_EQ(layoutType, entry.GetString("Not Set")) << "Layout type not set";
 }
 
-TEST_F(ShuffleboardInstanceTest, NestedActuatorWidgetsAreDisabled) {
+TEST(ShuffleboardInstanceTest, NestedActuatorWidgetsAreDisabled) {
+  NTWrapper ntInst;
+  frc::detail::ShuffleboardInstance shuffleboardInst{ntInst.inst};
+
   MockActuatorSendable sendable("Actuator");
-  m_shuffleboardInstance->GetTab("Tab")
-      .GetLayout("Title", "Type")
-      .Add(sendable);
+  shuffleboardInst.GetTab("Tab").GetLayout("Title", "Layout").Add(sendable);
   auto controllableEntry =
-      m_ntInstance.GetEntry("/Shuffleboard/Tab/Title/Actuator/.controllable");
-  m_shuffleboardInstance->Update();
+      ntInst.inst.GetEntry("/Shuffleboard/Tab/Title/Actuator/.controllable");
+  shuffleboardInst.Update();
 
   // Note: we use the unsafe `GetBoolean()` method because if the value is NOT
   // a boolean, or if it is not present, then something has clearly gone very,
@@ -98,7 +101,7 @@
   // Sanity check
   EXPECT_TRUE(controllable)
       << "The nested actuator widget should be enabled by default";
-  m_shuffleboardInstance->DisableActuatorWidgets();
+  shuffleboardInst.DisableActuatorWidgets();
   controllable = controllableEntry.GetValue()->GetBoolean();
   EXPECT_FALSE(controllable)
       << "The nested actuator widget should have been disabled";
diff --git a/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardTest.cpp b/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardTest.cpp
index d39d59d..693aac4 100644
--- a/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardTest.cpp
+++ b/wpilibc/src/test/native/cpp/shuffleboard/ShuffleboardTest.cpp
@@ -1,20 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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/shuffleboard/Shuffleboard.h"
-#include "frc/shuffleboard/ShuffleboardTab.h"
 #include "gtest/gtest.h"
 
-using namespace frc;
-
-class ShuffleboardTest : public testing::Test {};
-
-TEST_F(ShuffleboardTest, TabObjectsCached) {
-  ShuffleboardTab& tab1 = Shuffleboard::GetTab("testTabObjectsCached");
-  ShuffleboardTab& tab2 = Shuffleboard::GetTab("testTabObjectsCached");
+TEST(ShuffleboardTest, TabObjectsCached) {
+  auto& tab1 = frc::Shuffleboard::GetTab("testTabObjectsCached");
+  auto& tab2 = frc::Shuffleboard::GetTab("testTabObjectsCached");
   EXPECT_EQ(&tab1, &tab2) << "Tab objects were not cached";
 }
diff --git a/wpilibc/src/test/native/cpp/shuffleboard/SuppliedValueWidgetTest.cpp b/wpilibc/src/test/native/cpp/shuffleboard/SuppliedValueWidgetTest.cpp
index 8e39915..c8449e5 100644
--- a/wpilibc/src/test/native/cpp/shuffleboard/SuppliedValueWidgetTest.cpp
+++ b/wpilibc/src/test/native/cpp/shuffleboard/SuppliedValueWidgetTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <networktables/NetworkTableEntry.h>
 #include <networktables/NetworkTableInstance.h>
@@ -14,52 +11,55 @@
 
 using namespace frc;
 
-class SuppliedValueWidgetTest : public testing::Test {
-  void SetUp() override {
-    m_ntInstance = nt::NetworkTableInstance::Create();
-    m_instance = std::make_unique<detail::ShuffleboardInstance>(m_ntInstance);
-    m_tab = &(m_instance->GetTab("Tab"));
-  }
+class NTWrapper {
+ public:
+  NTWrapper() { inst = nt::NetworkTableInstance::Create(); }
 
+  ~NTWrapper() { nt::NetworkTableInstance::Destroy(inst); }
+
+  nt::NetworkTableInstance inst;
+};
+
+class SuppliedValueWidgetTest : public testing::Test {
  protected:
-  nt::NetworkTableInstance m_ntInstance;
-  ShuffleboardTab* m_tab;
-  std::unique_ptr<detail::ShuffleboardInstance> m_instance;
+  NTWrapper m_ntInst;
+  frc::detail::ShuffleboardInstance m_shuffleboardInst{m_ntInst.inst};
+  frc::ShuffleboardTab* m_tab = &(m_shuffleboardInst.GetTab("Tab"));
 };
 
 TEST_F(SuppliedValueWidgetTest, AddString) {
   std::string str = "foo";
   m_tab->AddString("String", [&str]() { return str; });
-  auto entry = m_ntInstance.GetEntry("/Shuffleboard/Tab/String");
+  auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/String");
 
-  m_instance->Update();
+  m_shuffleboardInst.Update();
   EXPECT_EQ("foo", entry.GetValue()->GetString());
 }
 
 TEST_F(SuppliedValueWidgetTest, AddNumber) {
   int num = 0;
   m_tab->AddNumber("Num", [&num]() { return ++num; });
-  auto entry = m_ntInstance.GetEntry("/Shuffleboard/Tab/Num");
+  auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Num");
 
-  m_instance->Update();
+  m_shuffleboardInst.Update();
   EXPECT_FLOAT_EQ(1.0, entry.GetValue()->GetDouble());
 }
 
 TEST_F(SuppliedValueWidgetTest, AddBoolean) {
   bool value = true;
   m_tab->AddBoolean("Bool", [&value]() { return value; });
-  auto entry = m_ntInstance.GetEntry("/Shuffleboard/Tab/Bool");
+  auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Bool");
 
-  m_instance->Update();
+  m_shuffleboardInst.Update();
   EXPECT_EQ(true, entry.GetValue()->GetBoolean());
 }
 
 TEST_F(SuppliedValueWidgetTest, AddStringArray) {
   std::vector<std::string> strings = {"foo", "bar"};
   m_tab->AddStringArray("Strings", [&strings]() { return strings; });
-  auto entry = m_ntInstance.GetEntry("/Shuffleboard/Tab/Strings");
+  auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Strings");
 
-  m_instance->Update();
+  m_shuffleboardInst.Update();
   auto actual = entry.GetValue()->GetStringArray();
 
   EXPECT_EQ(strings.size(), actual.size());
@@ -71,9 +71,9 @@
 TEST_F(SuppliedValueWidgetTest, AddNumberArray) {
   std::vector<double> nums = {0, 1, 2, 3};
   m_tab->AddNumberArray("Numbers", [&nums]() { return nums; });
-  auto entry = m_ntInstance.GetEntry("/Shuffleboard/Tab/Numbers");
+  auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Numbers");
 
-  m_instance->Update();
+  m_shuffleboardInst.Update();
   auto actual = entry.GetValue()->GetDoubleArray();
 
   EXPECT_EQ(nums.size(), actual.size());
@@ -85,9 +85,9 @@
 TEST_F(SuppliedValueWidgetTest, AddBooleanArray) {
   std::vector<int> bools = {true, false};
   m_tab->AddBooleanArray("Booleans", [&bools]() { return bools; });
-  auto entry = m_ntInstance.GetEntry("/Shuffleboard/Tab/Booleans");
+  auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Booleans");
 
-  m_instance->Update();
+  m_shuffleboardInst.Update();
   auto actual = entry.GetValue()->GetBooleanArray();
 
   EXPECT_EQ(bools.size(), actual.size());
@@ -97,11 +97,11 @@
 }
 
 TEST_F(SuppliedValueWidgetTest, AddRaw) {
-  wpi::StringRef bytes = "\1\2\3";
+  std::string_view bytes = "\1\2\3";
   m_tab->AddRaw("Raw", [&bytes]() { return bytes; });
-  auto entry = m_ntInstance.GetEntry("/Shuffleboard/Tab/Raw");
+  auto entry = m_ntInst.inst.GetEntry("/Shuffleboard/Tab/Raw");
 
-  m_instance->Update();
+  m_shuffleboardInst.Update();
   auto actual = entry.GetValue()->GetRaw();
   EXPECT_EQ(bytes, actual);
 }
diff --git a/wpilibc/src/test/native/cpp/simulation/ADXL345SimTest.cpp b/wpilibc/src/test/native/cpp/simulation/ADXL345SimTest.cpp
new file mode 100644
index 0000000..2bec0ed
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/ADXL345SimTest.cpp
@@ -0,0 +1,63 @@
+// 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/simulation/ADXL345Sim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "frc/ADXL345_I2C.h"
+#include "frc/ADXL345_SPI.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(ADXL345SimTest, SetSpiAttributes) {
+  HAL_Initialize(500, 0);
+
+  ADXL345_SPI accel(SPI::kMXP, Accelerometer::kRange_2G);
+  ADXL345Sim sim(accel);
+
+  EXPECT_EQ(0, accel.GetX());
+  EXPECT_EQ(0, accel.GetY());
+  EXPECT_EQ(0, accel.GetZ());
+
+  sim.SetX(1.91);
+  sim.SetY(-3.405);
+  sim.SetZ(2.29);
+
+  EXPECT_EQ(1.91, accel.GetX());
+  EXPECT_EQ(-3.405, accel.GetY());
+  EXPECT_EQ(2.29, accel.GetZ());
+
+  ADXL345_SPI::AllAxes allAccel = accel.GetAccelerations();
+  EXPECT_EQ(1.91, allAccel.XAxis);
+  EXPECT_EQ(-3.405, allAccel.YAxis);
+  EXPECT_EQ(2.29, allAccel.ZAxis);
+}
+
+TEST(ADXL345SimTest, SetI2CAttribute) {
+  HAL_Initialize(500, 0);
+
+  ADXL345_I2C accel(I2C::kMXP);
+  ADXL345Sim sim(accel);
+
+  EXPECT_EQ(0, accel.GetX());
+  EXPECT_EQ(0, accel.GetY());
+  EXPECT_EQ(0, accel.GetZ());
+
+  sim.SetX(1.91);
+  sim.SetY(-3.405);
+  sim.SetZ(2.29);
+
+  EXPECT_EQ(1.91, accel.GetX());
+  EXPECT_EQ(-3.405, accel.GetY());
+  EXPECT_EQ(2.29, accel.GetZ());
+
+  ADXL345_I2C::AllAxes allAccel = accel.GetAccelerations();
+  EXPECT_EQ(1.91, allAccel.XAxis);
+  EXPECT_EQ(-3.405, allAccel.YAxis);
+  EXPECT_EQ(2.29, allAccel.ZAxis);
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/ADXL362SimTest.cpp b/wpilibc/src/test/native/cpp/simulation/ADXL362SimTest.cpp
new file mode 100644
index 0000000..13f6c00
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/ADXL362SimTest.cpp
@@ -0,0 +1,38 @@
+// 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/simulation/ADXL362Sim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "frc/ADXL362.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(ADXL362SimTest, SetAttributes) {
+  HAL_Initialize(500, 0);
+
+  ADXL362 accel(SPI::kMXP, Accelerometer::kRange_2G);
+  ADXL362Sim sim(accel);
+
+  EXPECT_EQ(0, accel.GetX());
+  EXPECT_EQ(0, accel.GetY());
+  EXPECT_EQ(0, accel.GetZ());
+
+  sim.SetX(1.91);
+  sim.SetY(-3.405);
+  sim.SetZ(2.29);
+
+  EXPECT_EQ(1.91, accel.GetX());
+  EXPECT_EQ(-3.405, accel.GetY());
+  EXPECT_EQ(2.29, accel.GetZ());
+
+  ADXL362::AllAxes allAccel = accel.GetAccelerations();
+  EXPECT_EQ(1.91, allAccel.XAxis);
+  EXPECT_EQ(-3.405, allAccel.YAxis);
+  EXPECT_EQ(2.29, allAccel.ZAxis);
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/ADXRS450_GyroSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/ADXRS450_GyroSimTest.cpp
new file mode 100644
index 0000000..57c4fbe
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/ADXRS450_GyroSimTest.cpp
@@ -0,0 +1,35 @@
+// 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/simulation/ADXRS450_GyroSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "frc/ADXRS450_Gyro.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(ADXRS450GyroSimTest, SetAttributes) {
+  HAL_Initialize(500, 0);
+
+  ADXRS450_Gyro gyro;
+  ADXRS450_GyroSim sim{gyro};
+
+  EXPECT_EQ(0, gyro.GetAngle());
+  EXPECT_EQ(0, gyro.GetRate());
+
+  constexpr units::degree_t TEST_ANGLE{123.456};
+  constexpr units::degrees_per_second_t TEST_RATE{229.3504};
+  sim.SetAngle(TEST_ANGLE);
+  sim.SetRate(TEST_RATE);
+
+  EXPECT_EQ(TEST_ANGLE.value(), gyro.GetAngle());
+  EXPECT_EQ(TEST_RATE.value(), gyro.GetRate());
+
+  gyro.Reset();
+  EXPECT_EQ(0, gyro.GetAngle());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/AccelerometerSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/AccelerometerSimTest.cpp
index d2c7ea8..3dc8582 100644
--- a/wpilibc/src/test/native/cpp/simulation/AccelerometerSimTest.cpp
+++ b/wpilibc/src/test/native/cpp/simulation/AccelerometerSimTest.cpp
@@ -1,20 +1,19 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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/simulation/BuiltInAccelerometerSim.h"  // NOLINT(build/include_order)
 
 #include <hal/Accelerometer.h>
 #include <hal/HAL.h>
 
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/BuiltInAccelerometer.h"
 #include "gtest/gtest.h"
 
-using namespace frc::sim;
+namespace frc::sim {
 
-TEST(AcclerometerSimTests, TestActiveCallback) {
+TEST(AcclerometerSimTest, ActiveCallback) {
   HAL_Initialize(500, 0);
 
   BuiltInAccelerometerSim sim;
@@ -25,7 +24,7 @@
   bool lastValue = false;
 
   auto cb = sim.RegisterActiveCallback(
-      [&](wpi::StringRef name, const HAL_Value* value) {
+      [&](std::string_view name, const HAL_Value* value) {
         wasTriggered = true;
         lastValue = value->data.v_boolean;
       },
@@ -37,4 +36,103 @@
 
   EXPECT_TRUE(wasTriggered);
   EXPECT_TRUE(lastValue);
+  EXPECT_TRUE(sim.GetActive());
 }
+
+TEST(AcclerometerSimTest, SetX) {
+  HAL_Initialize(500, 0);
+  BuiltInAccelerometerSim sim;
+  sim.ResetData();
+
+  DoubleCallback callback;
+  constexpr double kTestValue = 1.91;
+
+  BuiltInAccelerometer accel;
+  auto cb = sim.RegisterXCallback(callback.GetCallback(), false);
+  sim.SetX(kTestValue);
+  EXPECT_EQ(kTestValue, accel.GetX());
+  EXPECT_EQ(kTestValue, sim.GetX());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTestValue, callback.GetLastValue());
+}
+
+TEST(AcclerometerSimTest, SetY) {
+  HAL_Initialize(500, 0);
+  BuiltInAccelerometerSim sim;
+  sim.ResetData();
+
+  DoubleCallback callback;
+  constexpr double kTestValue = 2.29;
+
+  BuiltInAccelerometer accel;
+  auto cb = sim.RegisterYCallback(callback.GetCallback(), false);
+  sim.SetY(kTestValue);
+  EXPECT_EQ(kTestValue, accel.GetY());
+  EXPECT_EQ(kTestValue, sim.GetY());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTestValue, callback.GetLastValue());
+}
+
+TEST(AcclerometerSimTest, SetZ) {
+  HAL_Initialize(500, 0);
+
+  BuiltInAccelerometer accel;
+  BuiltInAccelerometerSim sim(accel);
+  sim.ResetData();
+
+  DoubleCallback callback;
+  constexpr double kTestValue = 3.405;
+
+  auto cb = sim.RegisterZCallback(callback.GetCallback(), false);
+  sim.SetZ(kTestValue);
+  EXPECT_EQ(kTestValue, accel.GetZ());
+  EXPECT_EQ(kTestValue, sim.GetZ());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTestValue, callback.GetLastValue());
+}
+
+TEST(AcclerometerSimTest, SetRange) {
+  HAL_Initialize(500, 0);
+
+  BuiltInAccelerometerSim sim;
+  sim.ResetData();
+
+  EnumCallback callback;
+
+  Accelerometer::Range range = Accelerometer::kRange_4G;
+  auto cb = sim.RegisterRangeCallback(callback.GetCallback(), false);
+  BuiltInAccelerometer accel(range);
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(static_cast<int>(range), sim.GetRange());
+  EXPECT_EQ(static_cast<int>(range), callback.GetLastValue());
+
+  // 2G
+  callback.Reset();
+  range = Accelerometer::kRange_2G;
+  accel.SetRange(range);
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(static_cast<int>(range), sim.GetRange());
+  EXPECT_EQ(static_cast<int>(range), callback.GetLastValue());
+
+  // 4G
+  callback.Reset();
+  range = Accelerometer::kRange_4G;
+  accel.SetRange(range);
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(static_cast<int>(range), sim.GetRange());
+  EXPECT_EQ(static_cast<int>(range), callback.GetLastValue());
+
+  // 8G
+  callback.Reset();
+  range = Accelerometer::kRange_8G;
+  accel.SetRange(range);
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(static_cast<int>(range), sim.GetRange());
+  EXPECT_EQ(static_cast<int>(range), callback.GetLastValue());
+
+  // 16G - Not supported
+  callback.Reset();
+  EXPECT_THROW(accel.SetRange(Accelerometer::kRange_16G), std::runtime_error);
+  EXPECT_FALSE(callback.WasTriggered());
+}
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/AddressableLEDSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/AddressableLEDSimTest.cpp
new file mode 100644
index 0000000..ad4452e
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/AddressableLEDSimTest.cpp
@@ -0,0 +1,129 @@
+// 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/simulation/AddressableLEDSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/AddressableLED.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(AddressableLEDSimTest, InitializationCallback) {
+  HAL_Initialize(500, 0);
+
+  BooleanCallback callback;
+  AddressableLEDSim sim;
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+
+  EXPECT_FALSE(callback.WasTriggered());
+  AddressableLED led{0};
+
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(AddressableLEDSimTest, SetLength) {
+  HAL_Initialize(500, 0);
+
+  AddressableLED led{0};
+  AddressableLEDSim sim{led};
+  IntCallback callback;
+
+  auto cb = sim.RegisterLengthCallback(callback.GetCallback(), false);
+
+  EXPECT_EQ(1, sim.GetLength());  // Defaults to 1 led
+
+  std::array<AddressableLED::LEDData, 50> ledData;
+  led.SetLength(ledData.max_size());
+  led.SetData(ledData);
+
+  EXPECT_EQ(50, sim.GetLength());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(50, callback.GetLastValue());
+}
+
+TEST(AddressableLEDSimTest, SetRunning) {
+  HAL_Initialize(500, 0);
+
+  AddressableLEDSim sim = AddressableLEDSim::CreateForIndex(0);
+  BooleanCallback callback;
+  auto cb = sim.RegisterRunningCallback(callback.GetCallback(), false);
+  AddressableLED led{0};
+
+  EXPECT_FALSE(sim.GetRunning());
+
+  led.Start();
+  EXPECT_TRUE(sim.GetRunning());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+
+  callback.Reset();
+  led.Stop();
+  EXPECT_FALSE(sim.GetRunning());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_FALSE(callback.GetLastValue());
+}
+
+TEST(AddressableLEDSimTest, SetData) {
+  AddressableLED led{0};
+  AddressableLEDSim sim = AddressableLEDSim::CreateForChannel(0);
+
+  bool callbackHit = false;
+  std::array<AddressableLED::LEDData, 3> setData;
+  auto cb = sim.RegisterDataCallback(
+      [&](std::string_view, const unsigned char* buffer, unsigned int count) {
+        ASSERT_EQ(count, 12u);
+        EXPECT_EQ(0, buffer[0]);
+        EXPECT_EQ(0, buffer[1]);
+        EXPECT_EQ(255u, buffer[2]);
+        EXPECT_EQ(0, buffer[3]);
+
+        EXPECT_EQ(0, buffer[4]);
+        EXPECT_EQ(255u, buffer[5]);
+        EXPECT_EQ(0, buffer[6]);
+        EXPECT_EQ(0, buffer[7]);
+
+        EXPECT_EQ(255u, buffer[8]);
+        EXPECT_EQ(0, buffer[9]);
+        EXPECT_EQ(0, buffer[10]);
+        EXPECT_EQ(0, buffer[11]);
+
+        callbackHit = true;
+      },
+      false);
+
+  std::array<AddressableLED::LEDData, 3> ledData;
+  led.SetLength(ledData.max_size());
+
+  ledData[0].SetRGB(255, 0, 0);
+  ledData[1].SetRGB(0, 255, 0);
+  ledData[2].SetRGB(0, 0, 255);
+  led.SetData(ledData);
+
+  EXPECT_TRUE(callbackHit);
+
+  std::array<HAL_AddressableLEDData, 3> simData;
+  sim.GetData(simData.data());
+
+  EXPECT_EQ(0xFF, simData[0].r);
+  EXPECT_EQ(0x00, simData[0].g);
+  EXPECT_EQ(0x00, simData[0].b);
+  EXPECT_EQ(0x00, simData[0].padding);
+
+  EXPECT_EQ(0x00, simData[1].r);
+  EXPECT_EQ(0xFF, simData[1].g);
+  EXPECT_EQ(0x00, simData[1].b);
+  EXPECT_EQ(0x00, simData[1].padding);
+
+  EXPECT_EQ(0x00, simData[2].r);
+  EXPECT_EQ(0x00, simData[2].g);
+  EXPECT_EQ(0xFF, simData[2].b);
+  EXPECT_EQ(0x00, simData[2].padding);
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/AnalogEncoderSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/AnalogEncoderSimTest.cpp
index f32be7e..375e21a 100644
--- a/wpilibc/src/test/native/cpp/simulation/AnalogEncoderSimTest.cpp
+++ b/wpilibc/src/test/native/cpp/simulation/AnalogEncoderSimTest.cpp
@@ -1,13 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <hal/HAL.h>
 #include <units/math.h>
-#include <wpi/math>
+#include <wpi/numbers>
 
 #include "frc/AnalogEncoder.h"
 #include "frc/AnalogInput.h"
@@ -17,14 +14,14 @@
 #define EXPECT_NEAR_UNITS(val1, val2, eps) \
   EXPECT_LE(units::math::abs(val1 - val2), eps)
 
-TEST(AnalogEncoderSimTest, TestBasic) {
+TEST(AnalogEncoderSimTest, Basic) {
   frc::AnalogInput ai(0);
   frc::AnalogEncoder encoder{ai};
   frc::sim::AnalogEncoderSim encoderSim{encoder};
 
   encoderSim.SetPosition(180_deg);
-  EXPECT_NEAR(encoder.Get().to<double>(), 0.5, 1E-8);
-  EXPECT_NEAR(encoderSim.GetTurns().to<double>(), 0.5, 1E-8);
-  EXPECT_NEAR(encoderSim.GetPosition().Radians().to<double>(), wpi::math::pi,
+  EXPECT_NEAR(encoder.Get().value(), 0.5, 1E-8);
+  EXPECT_NEAR(encoderSim.GetTurns().value(), 0.5, 1E-8);
+  EXPECT_NEAR(encoderSim.GetPosition().Radians().value(), wpi::numbers::pi,
               1E-8);
 }
diff --git a/wpilibc/src/test/native/cpp/simulation/AnalogGyroSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/AnalogGyroSimTest.cpp
new file mode 100644
index 0000000..f1baaca
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/AnalogGyroSimTest.cpp
@@ -0,0 +1,86 @@
+// 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/simulation/AnalogGyroSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/AnalogGyro.h"
+#include "frc/AnalogInput.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(AnalogGyroSimTest, InitializeGyro) {
+  HAL_Initialize(500, 0);
+  AnalogGyroSim sim{0};
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback initializedCallback;
+
+  auto cb =
+      sim.RegisterInitializedCallback(initializedCallback.GetCallback(), false);
+  AnalogGyro gyro(0);
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(initializedCallback.WasTriggered());
+  EXPECT_TRUE(initializedCallback.GetLastValue());
+}
+
+TEST(AnalogGyroSimTest, SetAngle) {
+  HAL_Initialize(500, 0);
+
+  AnalogGyroSim sim{0};
+  DoubleCallback callback;
+
+  AnalogInput ai(0);
+  AnalogGyro gyro(&ai);
+  auto cb = sim.RegisterAngleCallback(callback.GetCallback(), false);
+  EXPECT_EQ(0, gyro.GetAngle());
+
+  constexpr double kTestAngle = 35.04;
+  sim.SetAngle(kTestAngle);
+  EXPECT_EQ(kTestAngle, sim.GetAngle());
+  EXPECT_EQ(kTestAngle, gyro.GetAngle());
+  EXPECT_EQ(-kTestAngle, gyro.GetRotation2d().Degrees().value());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTestAngle, callback.GetLastValue());
+}
+
+TEST(AnalogGyroSimTest, SetRate) {
+  HAL_Initialize(500, 0);
+
+  AnalogGyroSim sim{0};
+  DoubleCallback callback;
+
+  std::shared_ptr<AnalogInput> ai(new AnalogInput(0));
+  AnalogGyro gyro(ai);
+  auto cb = sim.RegisterRateCallback(callback.GetCallback(), false);
+  EXPECT_EQ(0, gyro.GetRate());
+
+  constexpr double kTestRate = -19.1;
+  sim.SetRate(kTestRate);
+  EXPECT_EQ(kTestRate, sim.GetRate());
+  EXPECT_EQ(kTestRate, gyro.GetRate());
+
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTestRate, callback.GetLastValue());
+}
+
+TEST(AnalogGyroSimTest, Reset) {
+  HAL_Initialize(500, 0);
+
+  AnalogInput ai{0};
+  AnalogGyro gyro(&ai);
+  AnalogGyroSim sim(gyro);
+  sim.SetAngle(12.34);
+  sim.SetRate(43.21);
+
+  sim.ResetData();
+  EXPECT_EQ(0, sim.GetAngle());
+  EXPECT_EQ(0, sim.GetRate());
+  EXPECT_EQ(0, gyro.GetAngle());
+  EXPECT_EQ(0, gyro.GetRate());
+}
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/AnalogInputSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/AnalogInputSimTest.cpp
new file mode 100644
index 0000000..8b4569e
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/AnalogInputSimTest.cpp
@@ -0,0 +1,188 @@
+// 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/simulation/AnalogInputSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/AnalogInput.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(AnalogInputSimTest, SetInitialized) {
+  HAL_Initialize(500, 0);
+
+  AnalogInputSim sim{5};
+  BooleanCallback callback;
+
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+  AnalogInput input{5};
+
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(AnalogInputSimTest, SetVoltage) {
+  HAL_Initialize(500, 0);
+
+  AnalogInputSim sim{5};
+  DoubleCallback callback;
+
+  auto cb = sim.RegisterVoltageCallback(callback.GetCallback(), false);
+  AnalogInput input{5};
+
+  for (int i = 0; i < 50; ++i) {
+    double voltage = i * .1;
+
+    callback.Reset();
+
+    sim.SetVoltage(0);
+    EXPECT_NEAR(sim.GetVoltage(), 0, 0.001) << " on " << i;
+    EXPECT_NEAR(input.GetVoltage(), 0, 0.001) << " on " << i;
+    // 0 -> 0 isn't a change, so callback not called
+    if (i > 2) {
+      EXPECT_TRUE(callback.WasTriggered()) << " on " << i;
+      EXPECT_EQ(0, callback.GetLastValue()) << " on " << i;
+    }
+
+    callback.Reset();
+    sim.SetVoltage(voltage);
+    EXPECT_NEAR(sim.GetVoltage(), voltage, 0.001) << " on " << i;
+    EXPECT_NEAR(input.GetVoltage(), voltage, 0.001) << " on " << i;
+
+    // 0 -> 0 isn't a change, so callback not called
+    if (i != 0) {
+      EXPECT_TRUE(callback.WasTriggered()) << " on " << i;
+      EXPECT_EQ(voltage, callback.GetLastValue()) << " on " << i;
+    }
+  }
+}
+
+TEST(AnalogInputSimTest, SetOverSampleBits) {
+  HAL_Initialize(500, 0);
+  AnalogInput input{5};
+  AnalogInputSim sim(input);
+
+  IntCallback callback;
+  auto cb = sim.RegisterOversampleBitsCallback(callback.GetCallback(), false);
+
+  input.SetOversampleBits(3504);
+  EXPECT_EQ(3504, sim.GetOversampleBits());
+  EXPECT_EQ(3504, input.GetOversampleBits());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(3504, callback.GetLastValue());
+}
+
+TEST(AnalogInputSimTest, SetAverageBits) {
+  HAL_Initialize(500, 0);
+  AnalogInput input{5};
+  AnalogInputSim sim(input);
+
+  IntCallback callback;
+  auto cb = sim.RegisterAverageBitsCallback(callback.GetCallback(), false);
+
+  input.SetAverageBits(3504);
+  EXPECT_EQ(3504, sim.GetAverageBits());
+  EXPECT_EQ(3504, input.GetAverageBits());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(3504, callback.GetLastValue());
+}
+
+TEST(AnalogInputSimTest, InitAccumulator) {
+  HAL_Initialize(500, 0);
+  AnalogInput input{0};
+  AnalogInputSim sim(input);
+
+  BooleanCallback callback;
+  auto cb =
+      sim.RegisterAccumulatorInitializedCallback(callback.GetCallback(), false);
+
+  input.InitAccumulator();
+  input.ResetAccumulator();
+  EXPECT_TRUE(sim.GetAccumulatorInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(AnalogInputSimTest, InitAccumulatorOnInvalidPort) {
+  HAL_Initialize(500, 0);
+  AnalogInput input{5};
+  AnalogInputSim sim(input);
+
+  BooleanCallback callback;
+  auto cb =
+      sim.RegisterAccumulatorInitializedCallback(callback.GetCallback(), false);
+
+  EXPECT_THROW(input.InitAccumulator(), std::runtime_error);
+  EXPECT_FALSE(callback.WasTriggered());
+}
+
+TEST(AnalogInputSimTest, SetAccumulatorValue) {
+  HAL_Initialize(500, 0);
+  AnalogInput input{0};
+  AnalogInputSim sim(input);
+
+  LongCallback callback;
+  auto cb = sim.RegisterAccumulatorValueCallback(callback.GetCallback(), false);
+
+  input.InitAccumulator();
+  sim.SetAccumulatorValue(3504191229);
+  EXPECT_EQ(3504191229, sim.GetAccumulatorValue());
+  EXPECT_EQ(3504191229, input.GetAccumulatorValue());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(3504191229, callback.GetLastValue());
+}
+
+TEST(AnalogInputSimTest, SetAccumulatorCount) {
+  HAL_Initialize(500, 0);
+  AnalogInput input{0};
+  AnalogInputSim sim(input);
+
+  LongCallback callback;
+  auto cb = sim.RegisterAccumulatorCountCallback(callback.GetCallback(), false);
+
+  input.InitAccumulator();
+  sim.SetAccumulatorCount(3504191229);
+  EXPECT_EQ(3504191229, sim.GetAccumulatorCount());
+  EXPECT_EQ(3504191229, input.GetAccumulatorCount());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(3504191229, callback.GetLastValue());
+}
+
+TEST(AnalogInputSimTest, SetAccumulatorDeadband) {
+  HAL_Initialize(500, 0);
+  AnalogInput input{0};
+  AnalogInputSim sim(input);
+
+  IntCallback callback;
+  auto cb =
+      sim.RegisterAccumulatorDeadbandCallback(callback.GetCallback(), false);
+
+  input.InitAccumulator();
+  input.SetAccumulatorDeadband(3504);
+  EXPECT_EQ(3504, sim.GetAccumulatorDeadband());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(3504, callback.GetLastValue());
+}
+
+TEST(AnalogInputSimTest, SetAccumulatorCenter) {
+  HAL_Initialize(500, 0);
+  AnalogInput input{0};
+  AnalogInputSim sim(input);
+
+  IntCallback callback;
+  auto cb =
+      sim.RegisterAccumulatorCenterCallback(callback.GetCallback(), false);
+
+  input.InitAccumulator();
+  input.SetAccumulatorCenter(3504);
+  EXPECT_EQ(3504, sim.GetAccumulatorCenter());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(3504, callback.GetLastValue());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/AnalogOutputSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/AnalogOutputSimTest.cpp
new file mode 100644
index 0000000..630f2c9
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/AnalogOutputSimTest.cpp
@@ -0,0 +1,89 @@
+// 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/simulation/AnalogOutputSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/AnalogOutput.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(AnalogOutputSimTest, Initialize) {
+  HAL_Initialize(500, 0);
+
+  AnalogOutputSim outputSim{0};
+  EXPECT_FALSE(outputSim.GetInitialized());
+
+  BooleanCallback callback;
+
+  auto cb =
+      outputSim.RegisterInitializedCallback(callback.GetCallback(), false);
+  AnalogOutput output(0);
+  EXPECT_TRUE(outputSim.GetInitialized());
+
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(AnalogOutputSimTest, SetCallback) {
+  HAL_Initialize(500, 0);
+
+  AnalogOutput output{0};
+  output.SetVoltage(0.5);
+
+  AnalogOutputSim outputSim(output);
+
+  DoubleCallback voltageCallback;
+
+  auto cb =
+      outputSim.RegisterVoltageCallback(voltageCallback.GetCallback(), false);
+
+  EXPECT_FALSE(voltageCallback.WasTriggered());
+
+  for (int i = 0; i < 50; ++i) {
+    double voltage = i * .1;
+    voltageCallback.Reset();
+
+    output.SetVoltage(0);
+
+    EXPECT_EQ(0, output.GetVoltage());
+    EXPECT_EQ(0, outputSim.GetVoltage());
+
+    // 0 -> 0 isn't a change, so callback not called
+    if (i > 2) {
+      EXPECT_TRUE(voltageCallback.WasTriggered()) << " on " << i;
+      EXPECT_NEAR(voltageCallback.GetLastValue(), 0, 0.001) << " on " << i;
+    }
+
+    voltageCallback.Reset();
+
+    output.SetVoltage(voltage);
+
+    EXPECT_EQ(voltage, output.GetVoltage());
+    EXPECT_EQ(voltage, outputSim.GetVoltage());
+
+    // 0 -> 0 isn't a change, so callback not called
+    if (i != 0) {
+      EXPECT_TRUE(voltageCallback.WasTriggered());
+      EXPECT_NEAR(voltageCallback.GetLastValue(), voltage, 0.001);
+    }
+  }
+}
+
+TEST(AnalogOutputSimTest, Reset) {
+  HAL_Initialize(500, 0);
+
+  AnalogOutputSim outputSim{0};
+
+  AnalogOutput output{0};
+  output.SetVoltage(1.2);
+
+  outputSim.ResetData();
+  EXPECT_EQ(0, output.GetVoltage());
+  EXPECT_EQ(0, outputSim.GetVoltage());
+}
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/AnalogTriggerSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/AnalogTriggerSimTest.cpp
new file mode 100644
index 0000000..5e9e28e
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/AnalogTriggerSimTest.cpp
@@ -0,0 +1,55 @@
+// 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/simulation/AnalogTriggerSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/AnalogTrigger.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(AnalogTriggerSimTest, Initialization) {
+  HAL_Initialize(500, 0);
+
+  AnalogTriggerSim sim = AnalogTriggerSim::CreateForIndex(0);
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+
+  AnalogTrigger trigger{0};
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(AnalogTriggerSimTest, TriggerLowerBound) {
+  HAL_Initialize(500, 0);
+
+  AnalogTrigger trigger{0};
+  AnalogTriggerSim sim(trigger);
+
+  DoubleCallback lowerCallback;
+  DoubleCallback upperCallback;
+  auto lowerCb =
+      sim.RegisterTriggerLowerBoundCallback(lowerCallback.GetCallback(), false);
+  auto upperCb =
+      sim.RegisterTriggerUpperBoundCallback(upperCallback.GetCallback(), false);
+
+  trigger.SetLimitsVoltage(0.299, 1.91);
+
+  EXPECT_EQ(0.299, sim.GetTriggerLowerBound());
+  EXPECT_EQ(1.91, sim.GetTriggerUpperBound());
+
+  EXPECT_TRUE(lowerCallback.WasTriggered());
+  EXPECT_EQ(0.299, lowerCallback.GetLastValue());
+
+  EXPECT_TRUE(upperCallback.WasTriggered());
+  EXPECT_EQ(1.91, upperCallback.GetLastValue());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/CTREPCMSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/CTREPCMSimTest.cpp
new file mode 100644
index 0000000..a8f95da
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/CTREPCMSimTest.cpp
@@ -0,0 +1,150 @@
+// 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/simulation/CTREPCMSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/DoubleSolenoid.h"
+#include "frc/PneumaticsControlModule.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(CTREPCMSimTest, InitializedCallback) {
+  CTREPCMSim sim;
+
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+
+  PneumaticsControlModule pcm;
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(CTREPCMSimTest, SolenoidOutput) {
+  PneumaticsControlModule pcm;
+  CTREPCMSim sim(pcm);
+  sim.ResetData();
+
+  DoubleSolenoid doubleSolenoid{0, frc::PneumaticsModuleType::CTREPCM, 3, 4};
+
+  BooleanCallback callback3;
+  BooleanCallback callback4;
+  auto cb3 =
+      sim.RegisterSolenoidOutputCallback(3, callback3.GetCallback(), false);
+  auto cb4 =
+      sim.RegisterSolenoidOutputCallback(4, callback4.GetCallback(), false);
+
+  callback3.Reset();
+  callback4.Reset();
+  doubleSolenoid.Set(DoubleSolenoid::kReverse);
+  EXPECT_FALSE(callback3.WasTriggered());
+  EXPECT_FALSE(callback3.GetLastValue());
+  EXPECT_TRUE(callback4.WasTriggered());
+  EXPECT_TRUE(callback4.GetLastValue());
+  EXPECT_FALSE(sim.GetSolenoidOutput(3));
+  EXPECT_TRUE(sim.GetSolenoidOutput(4));
+  EXPECT_EQ(0b00010000, pcm.GetSolenoids());
+  EXPECT_EQ(0b00010000, sim.GetAllSolenoidOutputs());
+
+  callback3.Reset();
+  callback4.Reset();
+  doubleSolenoid.Set(DoubleSolenoid::kForward);
+  EXPECT_TRUE(callback3.WasTriggered());
+  EXPECT_TRUE(callback3.GetLastValue());
+  EXPECT_TRUE(callback4.WasTriggered());
+  EXPECT_FALSE(callback4.GetLastValue());
+  EXPECT_TRUE(sim.GetSolenoidOutput(3));
+  EXPECT_FALSE(sim.GetSolenoidOutput(4));
+  EXPECT_EQ(0b00001000, pcm.GetSolenoids());
+  EXPECT_EQ(0b00001000, sim.GetAllSolenoidOutputs());
+
+  callback3.Reset();
+  callback4.Reset();
+  doubleSolenoid.Set(DoubleSolenoid::kOff);
+  EXPECT_TRUE(callback3.WasTriggered());
+  EXPECT_FALSE(callback3.GetLastValue());
+  EXPECT_FALSE(callback4.WasTriggered());
+  EXPECT_FALSE(callback4.GetLastValue());
+  EXPECT_FALSE(sim.GetSolenoidOutput(3));
+  EXPECT_FALSE(sim.GetSolenoidOutput(4));
+  EXPECT_EQ(0b00000000, pcm.GetSolenoids());
+  EXPECT_EQ(0b00000000, sim.GetAllSolenoidOutputs());
+}
+
+TEST(CTREPCMSimTest, SetCompressorOn) {
+  PneumaticsControlModule pcm;
+  CTREPCMSim sim(pcm);
+  sim.ResetData();
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterCompressorOnCallback(callback.GetCallback(), false);
+
+  EXPECT_FALSE(pcm.GetCompressor());
+  EXPECT_FALSE(pcm.GetCompressor());
+  sim.SetCompressorOn(true);
+  EXPECT_TRUE(sim.GetCompressorOn());
+  EXPECT_TRUE(pcm.GetCompressor());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(CTREPCMSimTest, SetClosedLoopEnabled) {
+  PneumaticsControlModule pcm;
+  CTREPCMSim sim(pcm);
+  sim.ResetData();
+
+  BooleanCallback callback;
+  auto cb =
+      sim.RegisterClosedLoopEnabledCallback(callback.GetCallback(), false);
+
+  pcm.SetClosedLoopControl(false);
+  EXPECT_FALSE(pcm.GetClosedLoopControl());
+
+  pcm.SetClosedLoopControl(true);
+  EXPECT_TRUE(sim.GetClosedLoopEnabled());
+  EXPECT_TRUE(pcm.GetClosedLoopControl());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(CTREPCMSimTest, SetPressureSwitchEnabled) {
+  PneumaticsControlModule pcm;
+  CTREPCMSim sim(pcm);
+  sim.ResetData();
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterPressureSwitchCallback(callback.GetCallback(), false);
+
+  EXPECT_FALSE(pcm.GetPressureSwitch());
+
+  sim.SetPressureSwitch(true);
+  EXPECT_TRUE(sim.GetPressureSwitch());
+  EXPECT_TRUE(pcm.GetPressureSwitch());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(CTREPCMSimTest, SetCompressorCurrent) {
+  PneumaticsControlModule pcm;
+  CTREPCMSim sim(pcm);
+  sim.ResetData();
+
+  DoubleCallback callback;
+  auto cb =
+      sim.RegisterCompressorCurrentCallback(callback.GetCallback(), false);
+
+  sim.SetCompressorCurrent(35.04);
+  EXPECT_EQ(35.04, sim.GetCompressorCurrent());
+  EXPECT_EQ(35.04, pcm.GetCompressorCurrent());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(35.04, callback.GetLastValue());
+}
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/DIOSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/DIOSimTest.cpp
new file mode 100644
index 0000000..338d24e
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/DIOSimTest.cpp
@@ -0,0 +1,80 @@
+// 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/simulation/DIOSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/DigitalInput.h"
+#include "frc/DigitalOutput.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(DIOSimTest, Initialization) {
+  HAL_Initialize(500, 0);
+  DIOSim sim{2};
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback initializeCallback;
+  BooleanCallback isInputCallback;
+
+  auto initializeCb =
+      sim.RegisterInitializedCallback(initializeCallback.GetCallback(), false);
+  auto inputCb =
+      sim.RegisterIsInputCallback(isInputCallback.GetCallback(), false);
+
+  DigitalOutput output(2);
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(initializeCallback.WasTriggered());
+  EXPECT_TRUE(initializeCallback.GetLastValue());
+  EXPECT_FALSE(sim.GetIsInput());
+  EXPECT_TRUE(isInputCallback.WasTriggered());
+  EXPECT_FALSE(isInputCallback.GetLastValue());
+
+  initializeCallback.Reset();
+  sim.SetInitialized(false);
+  EXPECT_TRUE(initializeCallback.WasTriggered());
+  EXPECT_FALSE(initializeCallback.GetLastValue());
+}
+
+TEST(DIOSimTest, Input) {
+  HAL_Initialize(500, 0);
+
+  DigitalInput input{0};
+  DIOSim sim(input);
+  EXPECT_TRUE(sim.GetIsInput());
+
+  BooleanCallback valueCallback;
+
+  auto cb = sim.RegisterValueCallback(valueCallback.GetCallback(), false);
+  EXPECT_TRUE(input.Get());
+  EXPECT_TRUE(sim.GetValue());
+
+  EXPECT_FALSE(valueCallback.WasTriggered());
+  sim.SetValue(false);
+  EXPECT_TRUE(valueCallback.WasTriggered());
+  EXPECT_FALSE(valueCallback.GetLastValue());
+}
+
+TEST(DIOSimTest, Output) {
+  HAL_Initialize(500, 0);
+  DigitalOutput output{0};
+  DIOSim sim(output);
+  EXPECT_FALSE(sim.GetIsInput());
+
+  BooleanCallback valueCallback;
+
+  auto cb = sim.RegisterValueCallback(valueCallback.GetCallback(), false);
+  EXPECT_FALSE(output.Get());
+  EXPECT_FALSE(sim.GetValue());
+
+  EXPECT_FALSE(valueCallback.WasTriggered());
+  output.Set(true);
+  EXPECT_TRUE(valueCallback.WasTriggered());
+  EXPECT_TRUE(valueCallback.GetLastValue());
+}
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/DifferentialDriveSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/DifferentialDriveSimTest.cpp
index f09344e..eabbecb 100644
--- a/wpilibc/src/test/native/cpp/simulation/DifferentialDriveSimTest.cpp
+++ b/wpilibc/src/test/native/cpp/simulation/DifferentialDriveSimTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <units/current.h>
 #include <units/math.h>
@@ -13,29 +10,30 @@
 #include "frc/controller/RamseteController.h"
 #include "frc/kinematics/DifferentialDriveKinematics.h"
 #include "frc/simulation/DifferentialDrivetrainSim.h"
-#include "frc/system/RungeKutta.h"
+#include "frc/system/NumericalIntegration.h"
 #include "frc/system/plant/DCMotor.h"
 #include "frc/system/plant/LinearSystemId.h"
 #include "frc/trajectory/TrajectoryGenerator.h"
 #include "frc/trajectory/constraint/DifferentialDriveKinematicsConstraint.h"
 #include "gtest/gtest.h"
 
-TEST(DifferentialDriveSim, Convergence) {
+TEST(DifferentialDriveSimTest, Convergence) {
   auto motor = frc::DCMotor::NEO(2);
   auto plant = frc::LinearSystemId::DrivetrainVelocitySystem(
       motor, 50_kg, 2_in, 12_in, 0.5_kg_sq_m, 1.0);
 
   frc::DifferentialDriveKinematics kinematics{24_in};
-  frc::sim::DifferentialDrivetrainSim sim{plant, 24_in, motor, 1.0, 2_in};
+  frc::sim::DifferentialDrivetrainSim sim{
+      plant, 24_in, motor,
+      1.0,   2_in,  {0.001, 0.001, 0.0001, 0.1, 0.1, 0.005, 0.005}};
 
   frc::LinearPlantInversionFeedforward feedforward{plant, 20_ms};
   frc::RamseteController ramsete;
 
-  feedforward.Reset(frc::MakeMatrix<2, 1>(0.0, 0.0));
+  feedforward.Reset(Eigen::Vector<double, 2>{0.0, 0.0});
 
   // Ground truth.
-  Eigen::Matrix<double, 7, 1> groundTruthX =
-      Eigen::Matrix<double, 7, 1>::Zero();
+  Eigen::Vector<double, 7> groundTruthX = Eigen::Vector<double, 7>::Zero();
 
   frc::TrajectoryConfig config{1_mps, 1_mps_sq};
   config.AddConstraint(
@@ -44,32 +42,35 @@
   auto trajectory = frc::TrajectoryGenerator::GenerateTrajectory(
       frc::Pose2d(), {}, frc::Pose2d(2_m, 2_m, 0_rad), config);
 
-  for (double t = 0; t < trajectory.TotalTime().to<double>(); t += 0.02) {
+  // NOLINTNEXTLINE
+  for (double t = 0; t < trajectory.TotalTime().value(); t += 0.02) {
     auto state = trajectory.Sample(20_ms);
     auto ramseteOut = ramsete.Calculate(sim.GetPose(), state);
 
     auto [l, r] = kinematics.ToWheelSpeeds(ramseteOut);
-    auto voltages = feedforward.Calculate(
-        frc::MakeMatrix<2, 1>(l.to<double>(), r.to<double>()));
+    auto voltages =
+        feedforward.Calculate(Eigen::Vector<double, 2>{l.value(), r.value()});
 
     // Sim periodic code.
     sim.SetInputs(units::volt_t(voltages(0, 0)), units::volt_t(voltages(1, 0)));
     sim.Update(20_ms);
 
     // Update ground truth.
-    groundTruthX = frc::RungeKutta(
-        [&sim](const auto& x, const auto& u) -> Eigen::Matrix<double, 7, 1> {
+    groundTruthX = frc::RK4(
+        [&sim](const auto& x, const auto& u) -> Eigen::Vector<double, 7> {
           return sim.Dynamics(x, u);
         },
         groundTruthX, voltages, 20_ms);
   }
 
-  EXPECT_NEAR(groundTruthX(0, 0), sim.GetState(0), 1E-6);
-  EXPECT_NEAR(groundTruthX(1, 0), sim.GetState(1), 1E-6);
-  EXPECT_NEAR(groundTruthX(2, 0), sim.GetState(2), 1E-6);
+  // 2 inch tolerance is OK since our ground truth is an approximation of the
+  // ODE solution using RK4 anyway
+  EXPECT_NEAR(groundTruthX(0, 0), sim.GetPose().X().value(), 0.05);
+  EXPECT_NEAR(groundTruthX(1, 0), sim.GetPose().Y().value(), 0.05);
+  EXPECT_NEAR(groundTruthX(2, 0), sim.GetHeading().Radians().value(), 0.01);
 }
 
-TEST(DifferentialDriveSim, Current) {
+TEST(DifferentialDriveSimTest, Current) {
   auto motor = frc::DCMotor::NEO(2);
   auto plant = frc::LinearSystemId::DrivetrainVelocitySystem(
       motor, 50_kg, 2_in, 12_in, 0.5_kg_sq_m, 1.0);
@@ -96,7 +97,7 @@
   EXPECT_TRUE(sim.GetCurrentDraw() > 0_A);
 }
 
-TEST(DifferentialDriveSim, ModelStability) {
+TEST(DifferentialDriveSimTest, ModelStability) {
   auto motor = frc::DCMotor::NEO(2);
   auto plant = frc::LinearSystemId::DrivetrainVelocitySystem(
       motor, 50_kg, 2_in, 12_in, 2_kg_sq_m, 5.0);
diff --git a/wpilibc/src/test/native/cpp/simulation/DigitalPWMSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/DigitalPWMSimTest.cpp
new file mode 100644
index 0000000..fd62edc
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/DigitalPWMSimTest.cpp
@@ -0,0 +1,57 @@
+// 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/simulation/DigitalPWMSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/DigitalOutput.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(DigitalPWMSimTest, Initialize) {
+  HAL_Initialize(500, 0);
+
+  DigitalOutput output{0};
+  DigitalPWMSim sim(output);
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback initializeCallback;
+  auto initCb =
+      sim.RegisterInitializedCallback(initializeCallback.GetCallback(), false);
+
+  DoubleCallback dutyCycleCallback;
+  auto dutyCycleCB =
+      sim.RegisterDutyCycleCallback(dutyCycleCallback.GetCallback(), false);
+
+  constexpr double kTestDutyCycle = 0.191;
+  output.EnablePWM(kTestDutyCycle);
+
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(initializeCallback.WasTriggered());
+  EXPECT_TRUE(initializeCallback.GetLastValue());
+
+  EXPECT_EQ(kTestDutyCycle, sim.GetDutyCycle());
+  EXPECT_TRUE(dutyCycleCallback.WasTriggered());
+  EXPECT_EQ(kTestDutyCycle, dutyCycleCallback.GetLastValue());
+}
+
+TEST(DigitalPWMSimTest, SetPin) {
+  HAL_Initialize(500, 0);
+
+  DigitalOutput output{2};
+  DigitalPWMSim sim(output);
+
+  IntCallback callback;
+  auto cb = sim.RegisterPinCallback(callback.GetCallback(), false);
+
+  sim.SetPin(191);
+  EXPECT_EQ(191, sim.GetPin());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(191, callback.GetLastValue());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp
new file mode 100644
index 0000000..d01ca27
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/DriverStationSimTest.cpp
@@ -0,0 +1,238 @@
+// 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 <string>
+#include <tuple>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/DriverStation.h"
+#include "frc/Joystick.h"
+#include "frc/RobotState.h"
+#include "frc/simulation/DriverStationSim.h"
+#include "frc/simulation/SimHooks.h"
+#include "gtest/gtest.h"
+
+using namespace frc;
+using namespace frc::sim;
+
+TEST(DriverStationTest, Enabled) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  EXPECT_FALSE(DriverStation::IsEnabled());
+  BooleanCallback callback;
+  auto cb =
+      DriverStationSim::RegisterEnabledCallback(callback.GetCallback(), false);
+  DriverStationSim::SetEnabled(true);
+  DriverStationSim::NotifyNewData();
+  EXPECT_TRUE(DriverStationSim::GetEnabled());
+  EXPECT_TRUE(DriverStation::IsEnabled());
+  EXPECT_TRUE(RobotState::IsEnabled());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(DriverStationTest, AutonomousMode) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  EXPECT_FALSE(DriverStation::IsAutonomous());
+  BooleanCallback callback;
+  auto cb = DriverStationSim::RegisterAutonomousCallback(callback.GetCallback(),
+                                                         false);
+  DriverStationSim::SetAutonomous(true);
+  DriverStationSim::NotifyNewData();
+  EXPECT_TRUE(DriverStationSim::GetAutonomous());
+  EXPECT_TRUE(DriverStation::IsAutonomous());
+  EXPECT_TRUE(RobotState::IsAutonomous());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(DriverStationTest, Mode) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  EXPECT_FALSE(DriverStation::IsTest());
+  BooleanCallback callback;
+  auto cb =
+      DriverStationSim::RegisterTestCallback(callback.GetCallback(), false);
+  DriverStationSim::SetTest(true);
+  DriverStationSim::NotifyNewData();
+  EXPECT_TRUE(DriverStationSim::GetTest());
+  EXPECT_TRUE(DriverStation::IsTest());
+  EXPECT_TRUE(RobotState::IsTest());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(DriverStationTest, Estop) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  EXPECT_FALSE(DriverStation::IsEStopped());
+  BooleanCallback callback;
+  auto cb =
+      DriverStationSim::RegisterEStopCallback(callback.GetCallback(), false);
+  DriverStationSim::SetEStop(true);
+  DriverStationSim::NotifyNewData();
+  EXPECT_TRUE(DriverStationSim::GetEStop());
+  EXPECT_TRUE(DriverStation::IsEStopped());
+  EXPECT_TRUE(RobotState::IsEStopped());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(DriverStationTest, FmsAttached) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  EXPECT_FALSE(DriverStation::IsFMSAttached());
+  BooleanCallback callback;
+  auto cb = DriverStationSim::RegisterFmsAttachedCallback(
+      callback.GetCallback(), false);
+  DriverStationSim::SetFmsAttached(true);
+  DriverStationSim::NotifyNewData();
+  EXPECT_TRUE(DriverStationSim::GetFmsAttached());
+  EXPECT_TRUE(DriverStation::IsFMSAttached());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(DriverStationTest, DsAttached) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  DriverStationSim::NotifyNewData();
+  EXPECT_TRUE(DriverStation::IsDSAttached());
+
+  BooleanCallback callback;
+  auto cb = DriverStationSim::RegisterDsAttachedCallback(callback.GetCallback(),
+                                                         false);
+  DriverStationSim::SetDsAttached(false);
+  DriverStationSim::NotifyNewData();
+  EXPECT_FALSE(DriverStationSim::GetDsAttached());
+  EXPECT_FALSE(DriverStation::IsDSAttached());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_FALSE(callback.GetLastValue());
+}
+
+TEST(DriverStationTest, AllianceStationId) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  EnumCallback callback;
+
+  HAL_AllianceStationID allianceStation = HAL_AllianceStationID_kBlue2;
+  DriverStationSim::SetAllianceStationId(allianceStation);
+
+  auto cb = DriverStationSim::RegisterAllianceStationIdCallback(
+      callback.GetCallback(), false);
+  // B1
+  allianceStation = HAL_AllianceStationID_kBlue1;
+  DriverStationSim::SetAllianceStationId(allianceStation);
+  EXPECT_EQ(allianceStation, DriverStationSim::GetAllianceStationId());
+  EXPECT_EQ(DriverStation::kBlue, DriverStation::GetAlliance());
+  EXPECT_EQ(1, DriverStation::GetLocation());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(allianceStation, callback.GetLastValue());
+
+  // B2
+  allianceStation = HAL_AllianceStationID_kBlue2;
+  DriverStationSim::SetAllianceStationId(allianceStation);
+  EXPECT_EQ(allianceStation, DriverStationSim::GetAllianceStationId());
+  EXPECT_EQ(DriverStation::kBlue, DriverStation::GetAlliance());
+  EXPECT_EQ(2, DriverStation::GetLocation());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(allianceStation, callback.GetLastValue());
+
+  // B3
+  allianceStation = HAL_AllianceStationID_kBlue3;
+  DriverStationSim::SetAllianceStationId(allianceStation);
+  EXPECT_EQ(allianceStation, DriverStationSim::GetAllianceStationId());
+  EXPECT_EQ(DriverStation::kBlue, DriverStation::GetAlliance());
+  EXPECT_EQ(3, DriverStation::GetLocation());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(allianceStation, callback.GetLastValue());
+
+  // R1
+  allianceStation = HAL_AllianceStationID_kRed1;
+  DriverStationSim::SetAllianceStationId(allianceStation);
+  EXPECT_EQ(allianceStation, DriverStationSim::GetAllianceStationId());
+  EXPECT_EQ(DriverStation::kRed, DriverStation::GetAlliance());
+  EXPECT_EQ(1, DriverStation::GetLocation());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(allianceStation, callback.GetLastValue());
+
+  // R2
+  allianceStation = HAL_AllianceStationID_kRed2;
+  DriverStationSim::SetAllianceStationId(allianceStation);
+  EXPECT_EQ(allianceStation, DriverStationSim::GetAllianceStationId());
+  EXPECT_EQ(DriverStation::kRed, DriverStation::GetAlliance());
+  EXPECT_EQ(2, DriverStation::GetLocation());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(allianceStation, callback.GetLastValue());
+
+  // R3
+  allianceStation = HAL_AllianceStationID_kRed3;
+  DriverStationSim::SetAllianceStationId(allianceStation);
+  EXPECT_EQ(allianceStation, DriverStationSim::GetAllianceStationId());
+  EXPECT_EQ(DriverStation::kRed, DriverStation::GetAlliance());
+  EXPECT_EQ(3, DriverStation::GetLocation());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(allianceStation, callback.GetLastValue());
+}
+
+TEST(DriverStationTest, ReplayNumber) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  DriverStationSim::SetReplayNumber(4);
+  DriverStationSim::NotifyNewData();
+  EXPECT_EQ(4, DriverStation::GetReplayNumber());
+}
+
+TEST(DriverStationTest, MatchNumber) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  DriverStationSim::SetMatchNumber(3);
+  DriverStationSim::NotifyNewData();
+  EXPECT_EQ(3, DriverStation::GetMatchNumber());
+}
+
+TEST(DriverStationTest, MatchTime) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  DoubleCallback callback;
+  auto cb = DriverStationSim::RegisterMatchTimeCallback(callback.GetCallback(),
+                                                        false);
+  constexpr double kTestTime = 19.174;
+  DriverStationSim::SetMatchTime(kTestTime);
+  EXPECT_EQ(kTestTime, DriverStationSim::GetMatchTime());
+  EXPECT_EQ(kTestTime, DriverStation::GetMatchTime());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTestTime, callback.GetLastValue());
+}
+
+TEST(DriverStationTest, SetGameSpecificMessage) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  constexpr auto message = "Hello World!";
+  DriverStationSim::SetGameSpecificMessage(message);
+  DriverStationSim::NotifyNewData();
+  EXPECT_EQ(message, DriverStation::GetGameSpecificMessage());
+}
+
+TEST(DriverStationTest, SetEventName) {
+  HAL_Initialize(500, 0);
+  DriverStationSim::ResetData();
+
+  constexpr auto message = "The Best Event";
+  DriverStationSim::SetEventName(message);
+  DriverStationSim::NotifyNewData();
+  EXPECT_EQ(message, DriverStation::GetEventName());
+}
diff --git a/wpilibc/src/test/native/cpp/simulation/DutyCycleEncoderSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/DutyCycleEncoderSimTest.cpp
new file mode 100644
index 0000000..8249499
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/DutyCycleEncoderSimTest.cpp
@@ -0,0 +1,35 @@
+// 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/simulation/DutyCycleEncoderSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/DutyCycleEncoder.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(DutyCycleEncoderSimTest, Set) {
+  HAL_Initialize(500, 0);
+
+  DutyCycleEncoder enc{0};
+  DutyCycleEncoderSim sim(enc);
+
+  constexpr units::turn_t kTestValue{5.67};
+  sim.Set(kTestValue);
+  EXPECT_EQ(kTestValue, enc.Get());
+}
+
+TEST(DutyCycleEncoderSimTest, SetDistance) {
+  HAL_Initialize(500, 0);
+
+  DutyCycleEncoder enc{0};
+  DutyCycleEncoderSim sim(enc);
+  sim.SetDistance(19.1);
+  EXPECT_EQ(19.1, enc.GetDistance());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/DutyCycleSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/DutyCycleSimTest.cpp
new file mode 100644
index 0000000..56e0592
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/DutyCycleSimTest.cpp
@@ -0,0 +1,70 @@
+// 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/simulation/DutyCycleSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/DigitalInput.h"
+#include "frc/DutyCycle.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(DutyCycleSimTest, Initialization) {
+  HAL_Initialize(500, 0);
+  DutyCycleSim sim = DutyCycleSim::CreateForIndex(0);
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+
+  DigitalInput di{2};
+  DutyCycle dc{&di};
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+
+  callback.Reset();
+  sim.SetInitialized(false);
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_FALSE(callback.GetLastValue());
+}
+
+TEST(DutyCycleSimTest, SetFrequency) {
+  HAL_Initialize(500, 0);
+
+  DigitalInput di{2};
+  DutyCycle dc{di};
+  DutyCycleSim sim(dc);
+
+  IntCallback callback;
+  auto cb = sim.RegisterFrequencyCallback(callback.GetCallback(), false);
+
+  sim.SetFrequency(191);
+  EXPECT_EQ(191, sim.GetFrequency());
+  EXPECT_EQ(191, dc.GetFrequency());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(191, callback.GetLastValue());
+}
+
+TEST(DutyCycleSimTest, SetOutput) {
+  HAL_Initialize(500, 0);
+
+  DigitalInput di{2};
+  DutyCycle dc{di};
+  DutyCycleSim sim(dc);
+
+  DoubleCallback callback;
+  auto cb = sim.RegisterOutputCallback(callback.GetCallback(), false);
+
+  sim.SetOutput(229.174);
+  EXPECT_EQ(229.174, sim.GetOutput());
+  EXPECT_EQ(229.174, dc.GetOutput());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(229.174, callback.GetLastValue());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp
index 05b0dc9..bfc50e1 100644
--- a/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp
+++ b/wpilibc/src/test/native/cpp/simulation/ElevatorSimTest.cpp
@@ -1,26 +1,23 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <iostream>
 
 #include <units/time.h>
 
 #include "frc/Encoder.h"
-#include "frc/PWMVictorSPX.h"
 #include "frc/RobotController.h"
-#include "frc/StateSpaceUtil.h"
 #include "frc/controller/PIDController.h"
+#include "frc/motorcontrol/PWMVictorSPX.h"
 #include "frc/simulation/ElevatorSim.h"
 #include "frc/simulation/EncoderSim.h"
+#include "frc/system/NumericalIntegration.h"
 #include "frc/system/plant/DCMotor.h"
 #include "frc/system/plant/LinearSystemId.h"
 #include "gtest/gtest.h"
 
-TEST(ElevatorSim, StateSpaceSim) {
+TEST(ElevatorSimTest, StateSpaceSim) {
   frc::sim::ElevatorSim sim(frc::DCMotor::Vex775Pro(4), 14.67, 8_kg,
                             units::meter_t(0.75 * 25.4 / 1000.0), 0_m, 3_m,
                             {0.01});
@@ -35,8 +32,8 @@
     auto nextVoltage = controller.Calculate(encoderSim.GetDistance());
     motor.Set(nextVoltage / frc::RobotController::GetInputVoltage());
 
-    auto u = frc::MakeMatrix<1, 1>(motor.Get() *
-                                   frc::RobotController::GetInputVoltage());
+    Eigen::Vector<double, 1> u{motor.Get() *
+                               frc::RobotController::GetInputVoltage()};
     sim.SetInput(u);
     sim.Update(20_ms);
 
@@ -44,15 +41,15 @@
     encoderSim.SetDistance(y(0));
   }
 
-  EXPECT_NEAR(controller.GetSetpoint(), sim.GetPosition().to<double>(), 0.2);
+  EXPECT_NEAR(controller.GetSetpoint(), sim.GetPosition().value(), 0.2);
 }
 
-TEST(ElevatorSim, MinMax) {
+TEST(ElevatorSimTest, MinMax) {
   frc::sim::ElevatorSim sim(frc::DCMotor::Vex775Pro(4), 14.67, 8_kg,
                             units::meter_t(0.75 * 25.4 / 1000.0), 0_m, 1_m,
                             {0.01});
   for (size_t i = 0; i < 100; ++i) {
-    sim.SetInput(frc::MakeMatrix<1, 1>(0.0));
+    sim.SetInput(Eigen::Vector<double, 1>{0.0});
     sim.Update(20_ms);
 
     auto height = sim.GetPosition();
@@ -60,10 +57,35 @@
   }
 
   for (size_t i = 0; i < 100; ++i) {
-    sim.SetInput(frc::MakeMatrix<1, 1>(12.0));
+    sim.SetInput(Eigen::Vector<double, 1>{12.0});
     sim.Update(20_ms);
 
     auto height = sim.GetPosition();
     EXPECT_TRUE(height < 1.05_m);
   }
 }
+
+TEST(ElevatorSimTest, Stability) {
+  static constexpr double kElevatorGearing = 100.0;
+  static constexpr units::meter_t kElevatorDrumRadius = 0.5_in;
+  static constexpr units::kilogram_t kCarriageMass = 4.0_kg;
+  frc::DCMotor m_elevatorGearbox = frc::DCMotor::Vex775Pro(4);
+
+  frc::LinearSystem<2, 1, 1> system = frc::LinearSystemId::ElevatorSystem(
+      m_elevatorGearbox, kCarriageMass, kElevatorDrumRadius, kElevatorGearing);
+
+  Eigen::Vector<double, 2> x0{0.0, 0.0};
+  Eigen::Vector<double, 1> u0{12.0};
+
+  Eigen::Vector<double, 2> x1{0.0, 0.0};
+  for (size_t i = 0; i < 50; i++) {
+    x1 = frc::RKDP(
+        [&](const Eigen::Vector<double, 2>& x,
+            const Eigen::Vector<double, 1>& u) -> Eigen::Vector<double, 2> {
+          return system.A() * x + system.B() * u;
+        },
+        x1, u0, 0.020_s);
+  }
+
+  EXPECT_NEAR(x1(0), system.CalculateX(x0, u0, 1_s)(0), 0.1);
+}
diff --git a/wpilibc/src/test/native/cpp/simulation/EncoderSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/EncoderSimTest.cpp
new file mode 100644
index 0000000..be13ca5
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/EncoderSimTest.cpp
@@ -0,0 +1,230 @@
+// 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/simulation/EncoderSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/Encoder.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+namespace {
+constexpr double kDefaultDistancePerPulse = .0005;
+}  // namespace
+
+TEST(EncoderSimTest, Initialize) {
+  HAL_Initialize(500, 0);
+
+  EncoderSim sim = EncoderSim::CreateForIndex(0);
+  sim.ResetData();
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+
+  Encoder encoder(0, 1);
+
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(EncoderSimTest, Rate) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  sim.SetRate(1.91);
+  EXPECT_EQ(1.91, sim.GetRate());
+}
+
+TEST(EncoderSimTest, Count) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  IntCallback callback;
+  auto cb = sim.RegisterCountCallback(callback.GetCallback(), false);
+  sim.SetCount(3504);
+  EXPECT_EQ(3504, sim.GetCount());
+
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(3504, encoder.Get());
+  EXPECT_EQ(3504, callback.GetLastValue());
+}
+
+TEST(EncoderSimTest, Distance) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  sim.SetDistance(229.174);
+  EXPECT_EQ(229.174, sim.GetDistance());
+  EXPECT_EQ(229.174, encoder.GetDistance());
+}
+
+TEST(EncoderSimTest, Period) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  DoubleCallback callback;
+  auto cb = sim.RegisterPeriodCallback(callback.GetCallback(), false);
+  sim.SetPeriod(123.456);
+  EXPECT_EQ(123.456, sim.GetPeriod());
+  EXPECT_EQ(123.456, encoder.GetPeriod().value());
+  EXPECT_EQ(kDefaultDistancePerPulse / 123.456, encoder.GetRate());
+
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(123.456, callback.GetLastValue());
+}
+
+TEST(EncoderSimTest, SetMaxPeriod) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  DoubleCallback callback;
+  auto cb = sim.RegisterMaxPeriodCallback(callback.GetCallback(), false);
+
+  encoder.SetMaxPeriod(units::second_t{123.456});
+  EXPECT_EQ(123.456, sim.GetMaxPeriod());
+
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(123.456, callback.GetLastValue());
+}
+
+TEST(EncoderSimTest, SetDirection) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterDirectionCallback(callback.GetCallback(), false);
+
+  sim.SetDirection(true);
+  EXPECT_TRUE(sim.GetDirection());
+  EXPECT_TRUE(encoder.GetDirection());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+
+  sim.SetDirection(false);
+  EXPECT_FALSE(sim.GetDirection());
+  EXPECT_FALSE(encoder.GetDirection());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_FALSE(callback.GetLastValue());
+}
+
+TEST(EncoderSimTest, SetReverseDirection) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterReverseDirectionCallback(callback.GetCallback(), false);
+
+  encoder.SetReverseDirection(true);
+  EXPECT_TRUE(sim.GetReverseDirection());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+
+  encoder.SetReverseDirection(false);
+  EXPECT_FALSE(sim.GetReverseDirection());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_FALSE(callback.GetLastValue());
+}
+
+TEST(EncoderSimTest, SetSamplesToAverage) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  IntCallback callback;
+  auto cb = sim.RegisterSamplesToAverageCallback(callback.GetCallback(), false);
+
+  encoder.SetSamplesToAverage(57);
+  EXPECT_EQ(57, sim.GetSamplesToAverage());
+  EXPECT_EQ(57, encoder.GetSamplesToAverage());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(57, callback.GetLastValue());
+}
+
+TEST(EncoderSimTest, SetDistancePerPulse) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  DoubleCallback callback;
+  auto cb = sim.RegisterDistancePerPulseCallback(callback.GetCallback(), false);
+
+  encoder.SetDistancePerPulse(.03405);
+  EXPECT_EQ(.03405, sim.GetDistancePerPulse());
+  EXPECT_EQ(.03405, encoder.GetDistancePerPulse());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(.03405, callback.GetLastValue());
+}
+
+TEST(EncoderSimTest, Reset) {
+  HAL_Initialize(500, 0);
+
+  Encoder encoder(0, 1);
+  EncoderSim sim(encoder);
+  sim.ResetData();
+
+  encoder.SetDistancePerPulse(kDefaultDistancePerPulse);
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterResetCallback(callback.GetCallback(), false);
+
+  sim.SetCount(3504);
+  sim.SetDistance(229.191);
+
+  encoder.Reset();
+  EXPECT_TRUE(sim.GetReset());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+
+  EXPECT_EQ(0, sim.GetCount());
+  EXPECT_EQ(0, encoder.Get());
+  EXPECT_EQ(0, sim.GetDistance());
+  EXPECT_EQ(0, encoder.GetDistance());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/PDPSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/PDPSimTest.cpp
new file mode 100644
index 0000000..78711b1
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/PDPSimTest.cpp
@@ -0,0 +1,82 @@
+// 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/simulation/PowerDistributionSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/PowerDistribution.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(PowerDistributionSimTest, Initialize) {
+  HAL_Initialize(500, 0);
+  PowerDistributionSim sim{2};
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback callback;
+
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+  PowerDistribution pdp(2, frc::PowerDistribution::ModuleType::kCTRE);
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+
+  callback.Reset();
+  sim.SetInitialized(false);
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_FALSE(callback.GetLastValue());
+}
+
+TEST(PowerDistributionSimTest, SetTemperature) {
+  HAL_Initialize(500, 0);
+  PowerDistribution pdp{2, frc::PowerDistribution::ModuleType::kCTRE};
+  PowerDistributionSim sim(pdp);
+
+  DoubleCallback callback;
+  auto cb = sim.RegisterTemperatureCallback(callback.GetCallback(), false);
+
+  sim.SetTemperature(35.04);
+  EXPECT_EQ(35.04, sim.GetTemperature());
+  EXPECT_EQ(35.04, pdp.GetTemperature());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(PowerDistributionSimTest, SetVoltage) {
+  HAL_Initialize(500, 0);
+  PowerDistribution pdp{2, frc::PowerDistribution::ModuleType::kCTRE};
+  PowerDistributionSim sim(pdp);
+
+  DoubleCallback callback;
+  auto cb = sim.RegisterVoltageCallback(callback.GetCallback(), false);
+
+  sim.SetVoltage(35.04);
+  EXPECT_EQ(35.04, sim.GetVoltage());
+  EXPECT_EQ(35.04, pdp.GetVoltage());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(PowerDistributionSimTest, SetCurrent) {
+  HAL_Initialize(500, 0);
+  PowerDistribution pdp{2, frc::PowerDistribution::ModuleType::kCTRE};
+  PowerDistributionSim sim(pdp);
+
+  for (int channel = 0; channel < HAL_GetNumCTREPDPChannels(); ++channel) {
+    DoubleCallback callback;
+    auto cb =
+        sim.RegisterCurrentCallback(channel, callback.GetCallback(), false);
+
+    const double kTestCurrent = 35.04 + channel;
+    sim.SetCurrent(channel, kTestCurrent);
+    EXPECT_EQ(kTestCurrent, sim.GetCurrent(channel));
+    EXPECT_EQ(kTestCurrent, pdp.GetCurrent(channel));
+    EXPECT_TRUE(callback.WasTriggered());
+    EXPECT_TRUE(callback.GetLastValue());
+  }
+}
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/PWMSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/PWMSimTest.cpp
new file mode 100644
index 0000000..0df3590
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/PWMSimTest.cpp
@@ -0,0 +1,120 @@
+// 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/simulation/PWMSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/PWM.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(PWMSimTest, Initialize) {
+  HAL_Initialize(500, 0);
+
+  PWMSim sim{0};
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback callback;
+
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+  PWM pwm{0};
+  EXPECT_TRUE(sim.GetInitialized());
+}
+
+TEST(PWMSimTest, SetRawValue) {
+  HAL_Initialize(500, 0);
+
+  PWMSim sim{0};
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  IntCallback callback;
+
+  auto cb = sim.RegisterRawValueCallback(callback.GetCallback(), false);
+  PWM pwm{0};
+  sim.SetRawValue(229);
+  EXPECT_EQ(229, sim.GetRawValue());
+  EXPECT_EQ(229, pwm.GetRaw());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(229, callback.GetLastValue());
+}
+
+TEST(PWMSimTest, SetSpeed) {
+  HAL_Initialize(500, 0);
+
+  PWMSim sim{0};
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  DoubleCallback callback;
+
+  auto cb = sim.RegisterSpeedCallback(callback.GetCallback(), false);
+  PWM pwm{0};
+  constexpr double kTestValue = 0.3504;
+  pwm.SetSpeed(kTestValue);
+
+  EXPECT_EQ(kTestValue, sim.GetSpeed());
+  EXPECT_EQ(kTestValue, pwm.GetSpeed());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTestValue, callback.GetLastValue());
+}
+
+TEST(PWMSimTest, SetPosition) {
+  HAL_Initialize(500, 0);
+
+  PWMSim sim{0};
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  DoubleCallback callback;
+
+  auto cb = sim.RegisterPositionCallback(callback.GetCallback(), false);
+  PWM pwm{0};
+  constexpr double kTestValue = 0.3504;
+  pwm.SetPosition(kTestValue);
+
+  EXPECT_EQ(kTestValue, sim.GetPosition());
+  EXPECT_EQ(kTestValue, pwm.GetPosition());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(kTestValue, callback.GetLastValue());
+}
+
+TEST(PWMSimTest, SetPeriodScale) {
+  HAL_Initialize(500, 0);
+
+  PWMSim sim{0};
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  IntCallback callback;
+
+  auto cb = sim.RegisterPeriodScaleCallback(callback.GetCallback(), false);
+  PWM pwm{0};
+  sim.SetPeriodScale(3504);
+  EXPECT_EQ(3504, sim.GetPeriodScale());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(3504, callback.GetLastValue());
+}
+
+TEST(PWMSimTest, SetZeroLatch) {
+  HAL_Initialize(500, 0);
+
+  PWMSim sim{0};
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback callback;
+
+  auto cb = sim.RegisterZeroLatchCallback(callback.GetCallback(), false);
+  PWM pwm{0};
+  pwm.SetZeroLatch();
+
+  EXPECT_TRUE(callback.WasTriggered());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp
new file mode 100644
index 0000000..7786b29
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/REVPHSimTest.cpp
@@ -0,0 +1,150 @@
+// 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/simulation/REVPHSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/DoubleSolenoid.h"
+#include "frc/PneumaticHub.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(REVPHSimTest, InitializedCallback) {
+  REVPHSim sim;
+
+  sim.ResetData();
+  EXPECT_FALSE(sim.GetInitialized());
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterInitializedCallback(callback.GetCallback(), false);
+
+  PneumaticHub ph;
+  EXPECT_TRUE(sim.GetInitialized());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(REVPHSimTest, SolenoidOutput) {
+  PneumaticHub ph;
+  REVPHSim sim(ph);
+  sim.ResetData();
+
+  DoubleSolenoid doubleSolenoid{1, frc::PneumaticsModuleType::REVPH, 3, 4};
+
+  BooleanCallback callback3;
+  BooleanCallback callback4;
+  auto cb3 =
+      sim.RegisterSolenoidOutputCallback(3, callback3.GetCallback(), false);
+  auto cb4 =
+      sim.RegisterSolenoidOutputCallback(4, callback4.GetCallback(), false);
+
+  callback3.Reset();
+  callback4.Reset();
+  doubleSolenoid.Set(DoubleSolenoid::kReverse);
+  EXPECT_FALSE(callback3.WasTriggered());
+  EXPECT_FALSE(callback3.GetLastValue());
+  EXPECT_TRUE(callback4.WasTriggered());
+  EXPECT_TRUE(callback4.GetLastValue());
+  EXPECT_FALSE(sim.GetSolenoidOutput(3));
+  EXPECT_TRUE(sim.GetSolenoidOutput(4));
+  EXPECT_EQ(0b00010000, ph.GetSolenoids());
+  EXPECT_EQ(0b00010000, sim.GetAllSolenoidOutputs());
+
+  callback3.Reset();
+  callback4.Reset();
+  doubleSolenoid.Set(DoubleSolenoid::kForward);
+  EXPECT_TRUE(callback3.WasTriggered());
+  EXPECT_TRUE(callback3.GetLastValue());
+  EXPECT_TRUE(callback4.WasTriggered());
+  EXPECT_FALSE(callback4.GetLastValue());
+  EXPECT_TRUE(sim.GetSolenoidOutput(3));
+  EXPECT_FALSE(sim.GetSolenoidOutput(4));
+  EXPECT_EQ(0b00001000, ph.GetSolenoids());
+  EXPECT_EQ(0b00001000, sim.GetAllSolenoidOutputs());
+
+  callback3.Reset();
+  callback4.Reset();
+  doubleSolenoid.Set(DoubleSolenoid::kOff);
+  EXPECT_TRUE(callback3.WasTriggered());
+  EXPECT_FALSE(callback3.GetLastValue());
+  EXPECT_FALSE(callback4.WasTriggered());
+  EXPECT_FALSE(callback4.GetLastValue());
+  EXPECT_FALSE(sim.GetSolenoidOutput(3));
+  EXPECT_FALSE(sim.GetSolenoidOutput(4));
+  EXPECT_EQ(0b00000000, ph.GetSolenoids());
+  EXPECT_EQ(0b00000000, sim.GetAllSolenoidOutputs());
+}
+
+TEST(REVPHSimTest, SetCompressorOn) {
+  PneumaticHub ph;
+  REVPHSim sim(ph);
+  sim.ResetData();
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterCompressorOnCallback(callback.GetCallback(), false);
+
+  EXPECT_FALSE(ph.GetCompressor());
+  EXPECT_FALSE(ph.GetCompressor());
+  sim.SetCompressorOn(true);
+  EXPECT_TRUE(sim.GetCompressorOn());
+  EXPECT_TRUE(ph.GetCompressor());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(REVPHSimTest, SetClosedLoopEnabled) {
+  PneumaticHub ph;
+  REVPHSim sim(ph);
+  sim.ResetData();
+
+  BooleanCallback callback;
+  auto cb =
+      sim.RegisterClosedLoopEnabledCallback(callback.GetCallback(), false);
+
+  ph.SetClosedLoopControl(false);
+  EXPECT_FALSE(ph.GetClosedLoopControl());
+
+  ph.SetClosedLoopControl(true);
+  EXPECT_TRUE(sim.GetClosedLoopEnabled());
+  EXPECT_TRUE(ph.GetClosedLoopControl());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(REVPHSimTest, SetPressureSwitchEnabled) {
+  PneumaticHub ph;
+  REVPHSim sim(ph);
+  sim.ResetData();
+
+  BooleanCallback callback;
+  auto cb = sim.RegisterPressureSwitchCallback(callback.GetCallback(), false);
+
+  EXPECT_FALSE(ph.GetPressureSwitch());
+
+  sim.SetPressureSwitch(true);
+  EXPECT_TRUE(sim.GetPressureSwitch());
+  EXPECT_TRUE(ph.GetPressureSwitch());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+}
+
+TEST(REVPHSimTest, SetCompressorCurrent) {
+  PneumaticHub ph;
+  REVPHSim sim(ph);
+  sim.ResetData();
+
+  DoubleCallback callback;
+  auto cb =
+      sim.RegisterCompressorCurrentCallback(callback.GetCallback(), false);
+
+  sim.SetCompressorCurrent(35.04);
+  EXPECT_EQ(35.04, sim.GetCompressorCurrent());
+  EXPECT_EQ(35.04, ph.GetCompressorCurrent());
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_EQ(35.04, callback.GetLastValue());
+}
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/RelaySimTest.cpp b/wpilibc/src/test/native/cpp/simulation/RelaySimTest.cpp
new file mode 100644
index 0000000..292d629
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/RelaySimTest.cpp
@@ -0,0 +1,204 @@
+// 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/simulation/RelaySim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/Relay.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(RelaySimTest, InitializationBidrectional) {
+  HAL_Initialize(500, 0);
+
+  RelaySim sim(0);
+  sim.ResetData();
+
+  BooleanCallback forwardCallback;
+  BooleanCallback reverseCallback;
+
+  EXPECT_FALSE(sim.GetInitializedForward());
+  EXPECT_FALSE(sim.GetInitializedReverse());
+
+  auto fwdCb = sim.RegisterInitializedForwardCallback(
+      forwardCallback.GetCallback(), false);
+  auto revCb = sim.RegisterInitializedReverseCallback(
+      reverseCallback.GetCallback(), false);
+  Relay relay{0};
+
+  EXPECT_TRUE(sim.GetInitializedForward());
+  EXPECT_TRUE(sim.GetInitializedReverse());
+
+  EXPECT_TRUE(forwardCallback.WasTriggered());
+  EXPECT_TRUE(forwardCallback.GetLastValue());
+  EXPECT_TRUE(reverseCallback.WasTriggered());
+  EXPECT_TRUE(reverseCallback.GetLastValue());
+}
+
+TEST(RelaySimTest, InitializationForwardOnly) {
+  HAL_Initialize(500, 0);
+
+  RelaySim sim{0};
+  sim.ResetData();
+
+  BooleanCallback forwardCallback;
+  BooleanCallback reverseCallback;
+
+  EXPECT_FALSE(sim.GetInitializedForward());
+  EXPECT_FALSE(sim.GetInitializedReverse());
+
+  auto fwdCb = sim.RegisterInitializedForwardCallback(
+      forwardCallback.GetCallback(), false);
+  auto revCb = sim.RegisterInitializedReverseCallback(
+      reverseCallback.GetCallback(), false);
+  Relay relay(0, Relay::kForwardOnly);
+
+  EXPECT_TRUE(sim.GetInitializedForward());
+  EXPECT_FALSE(sim.GetInitializedReverse());
+
+  EXPECT_TRUE(forwardCallback.WasTriggered());
+  EXPECT_TRUE(forwardCallback.GetLastValue());
+  EXPECT_FALSE(reverseCallback.WasTriggered());
+}
+
+TEST(RelaySimTest, InitializationReverseOnly) {
+  HAL_Initialize(500, 0);
+
+  RelaySim sim{0};
+  sim.ResetData();
+
+  BooleanCallback forwardCallback;
+  BooleanCallback reverseCallback;
+
+  EXPECT_FALSE(sim.GetInitializedForward());
+  EXPECT_FALSE(sim.GetInitializedReverse());
+
+  auto fwdCb = sim.RegisterInitializedForwardCallback(
+      forwardCallback.GetCallback(), false);
+  auto revCb = sim.RegisterInitializedReverseCallback(
+      reverseCallback.GetCallback(), false);
+  Relay relay(0, Relay::kReverseOnly);
+
+  EXPECT_FALSE(sim.GetInitializedForward());
+  EXPECT_TRUE(sim.GetInitializedReverse());
+
+  EXPECT_FALSE(forwardCallback.WasTriggered());
+  EXPECT_TRUE(reverseCallback.WasTriggered());
+  EXPECT_TRUE(reverseCallback.GetLastValue());
+}
+
+TEST(RelaySimTest, BidirectionalSetForward) {
+  HAL_Initialize(500, 0);
+
+  RelaySim sim{0};
+  BooleanCallback forwardCallback;
+  BooleanCallback reverseCallback;
+
+  Relay relay{0};
+  auto fwdCb =
+      sim.RegisterForwardCallback(forwardCallback.GetCallback(), false);
+  auto revCb =
+      sim.RegisterReverseCallback(reverseCallback.GetCallback(), false);
+
+  relay.Set(Relay::kForward);
+  EXPECT_EQ(Relay::kForward, relay.Get());
+  EXPECT_TRUE(sim.GetForward());
+  EXPECT_FALSE(sim.GetReverse());
+
+  EXPECT_TRUE(forwardCallback.WasTriggered());
+  EXPECT_TRUE(forwardCallback.GetLastValue());
+  EXPECT_FALSE(reverseCallback.WasTriggered());
+}
+
+TEST(RelaySimTest, BidirectionalSetReverse) {
+  HAL_Initialize(500, 0);
+
+  RelaySim sim{0};
+  BooleanCallback forwardCallback;
+  BooleanCallback reverseCallback;
+
+  Relay relay{0};
+  auto fwdCb =
+      sim.RegisterForwardCallback(forwardCallback.GetCallback(), false);
+  auto revCb =
+      sim.RegisterReverseCallback(reverseCallback.GetCallback(), false);
+
+  relay.Set(Relay::kReverse);
+  EXPECT_EQ(Relay::kReverse, relay.Get());
+  EXPECT_FALSE(sim.GetForward());
+  EXPECT_TRUE(sim.GetReverse());
+
+  EXPECT_FALSE(forwardCallback.WasTriggered());
+  EXPECT_TRUE(reverseCallback.WasTriggered());
+  EXPECT_TRUE(reverseCallback.GetLastValue());
+}
+
+TEST(RelaySimTest, BidirectionalSetOn) {
+  HAL_Initialize(500, 0);
+
+  RelaySim sim{0};
+  BooleanCallback forwardCallback;
+  BooleanCallback reverseCallback;
+
+  Relay relay{0};
+  auto fwdCb =
+      sim.RegisterForwardCallback(forwardCallback.GetCallback(), false);
+  auto revCb =
+      sim.RegisterReverseCallback(reverseCallback.GetCallback(), false);
+
+  relay.Set(Relay::kOn);
+  EXPECT_EQ(Relay::kOn, relay.Get());
+  EXPECT_TRUE(sim.GetForward());
+  EXPECT_TRUE(sim.GetReverse());
+
+  EXPECT_TRUE(forwardCallback.WasTriggered());
+  EXPECT_TRUE(forwardCallback.GetLastValue());
+  EXPECT_TRUE(reverseCallback.WasTriggered());
+  EXPECT_TRUE(reverseCallback.GetLastValue());
+}
+
+TEST(RelaySimTest, BidirectionalSetOff) {
+  HAL_Initialize(500, 0);
+
+  RelaySim sim{0};
+  BooleanCallback forwardCallback;
+  BooleanCallback reverseCallback;
+
+  Relay relay{0};
+  auto fwdCb =
+      sim.RegisterForwardCallback(forwardCallback.GetCallback(), false);
+  auto revCb =
+      sim.RegisterReverseCallback(reverseCallback.GetCallback(), false);
+
+  // Bootstrap into a non-off state to verify the callbacks
+  relay.Set(Relay::kOn);
+  forwardCallback.Reset();
+  reverseCallback.Reset();
+
+  relay.Set(Relay::kOff);
+  EXPECT_EQ(Relay::kOff, relay.Get());
+  EXPECT_FALSE(sim.GetForward());
+  EXPECT_FALSE(sim.GetReverse());
+
+  EXPECT_TRUE(forwardCallback.WasTriggered());
+  EXPECT_FALSE(forwardCallback.GetLastValue());
+  EXPECT_TRUE(reverseCallback.WasTriggered());
+  EXPECT_FALSE(reverseCallback.GetLastValue());
+}
+
+TEST(RelaySimTest, StopMotor) {
+  Relay relay{0};
+  RelaySim sim(relay);
+
+  // Bootstrap into non-off state
+  relay.Set(Relay::kOn);
+
+  relay.StopMotor();
+  EXPECT_EQ(Relay::kOff, relay.Get());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp
new file mode 100644
index 0000000..c6e93d4
--- /dev/null
+++ b/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp
@@ -0,0 +1,210 @@
+// 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/simulation/RoboRioSim.h"  // NOLINT(build/include_order)
+
+#include <hal/HAL.h>
+#include <hal/HALBase.h>
+
+#include "callback_helpers/TestCallbackHelpers.h"
+#include "frc/RobotController.h"
+#include "gtest/gtest.h"
+
+namespace frc::sim {
+
+TEST(RoboRioSimTest, FPGAButton) {
+  RoboRioSim::ResetData();
+
+  int dummyStatus = 0;
+
+  BooleanCallback callback;
+  auto cb =
+      RoboRioSim::RegisterFPGAButtonCallback(callback.GetCallback(), false);
+  RoboRioSim::SetFPGAButton(true);
+  EXPECT_TRUE(RoboRioSim::GetFPGAButton());
+  EXPECT_TRUE(HAL_GetFPGAButton(&dummyStatus));
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_TRUE(callback.GetLastValue());
+
+  callback.Reset();
+  RoboRioSim::SetFPGAButton(false);
+  EXPECT_FALSE(RoboRioSim::GetFPGAButton());
+  EXPECT_FALSE(HAL_GetFPGAButton(&dummyStatus));
+  EXPECT_TRUE(callback.WasTriggered());
+  EXPECT_FALSE(callback.GetLastValue());
+}
+
+TEST(RoboRioSimTest, SetVin) {
+  RoboRioSim::ResetData();
+
+  DoubleCallback voltageCallback;
+  DoubleCallback currentCallback;
+  auto voltageCb = RoboRioSim::RegisterVInVoltageCallback(
+      voltageCallback.GetCallback(), false);
+  auto currentCb = RoboRioSim::RegisterVInCurrentCallback(
+      currentCallback.GetCallback(), false);
+  constexpr double kTestVoltage = 1.91;
+  constexpr double kTestCurrent = 35.04;
+
+  RoboRioSim::SetVInVoltage(units::volt_t{kTestVoltage});
+  EXPECT_TRUE(voltageCallback.WasTriggered());
+  EXPECT_EQ(kTestVoltage, voltageCallback.GetLastValue());
+  EXPECT_EQ(kTestVoltage, RoboRioSim::GetVInVoltage().value());
+  EXPECT_EQ(kTestVoltage, RobotController::GetInputVoltage());
+
+  RoboRioSim::SetVInCurrent(units::ampere_t{kTestCurrent});
+  EXPECT_TRUE(currentCallback.WasTriggered());
+  EXPECT_EQ(kTestCurrent, currentCallback.GetLastValue());
+  EXPECT_EQ(kTestCurrent, RoboRioSim::GetVInCurrent().value());
+  EXPECT_EQ(kTestCurrent, RobotController::GetInputCurrent());
+}
+
+TEST(RoboRioSimTest, SetBrownout) {
+  RoboRioSim::ResetData();
+
+  DoubleCallback voltageCallback;
+  auto voltageCb = RoboRioSim::RegisterBrownoutVoltageCallback(
+      voltageCallback.GetCallback(), false);
+  constexpr double kTestVoltage = 1.91;
+
+  RoboRioSim::SetBrownoutVoltage(units::volt_t{kTestVoltage});
+  EXPECT_TRUE(voltageCallback.WasTriggered());
+  EXPECT_EQ(kTestVoltage, voltageCallback.GetLastValue());
+  EXPECT_EQ(kTestVoltage, RoboRioSim::GetBrownoutVoltage().value());
+  EXPECT_EQ(kTestVoltage, RobotController::GetBrownoutVoltage().value());
+}
+
+TEST(RoboRioSimTest, Set6V) {
+  RoboRioSim::ResetData();
+
+  DoubleCallback voltageCallback;
+  DoubleCallback currentCallback;
+  BooleanCallback activeCallback;
+  IntCallback faultCallback;
+  auto voltageCb = RoboRioSim::RegisterUserVoltage6VCallback(
+      voltageCallback.GetCallback(), false);
+  auto currentCb = RoboRioSim::RegisterUserCurrent6VCallback(
+      currentCallback.GetCallback(), false);
+  auto activeCb = RoboRioSim::RegisterUserActive6VCallback(
+      activeCallback.GetCallback(), false);
+  auto faultsCb = RoboRioSim::RegisterUserFaults6VCallback(
+      faultCallback.GetCallback(), false);
+  constexpr double kTestVoltage = 22.9;
+  constexpr double kTestCurrent = 174;
+  constexpr int kTestFaults = 229;
+
+  RoboRioSim::SetUserVoltage6V(units::volt_t{kTestVoltage});
+  EXPECT_TRUE(voltageCallback.WasTriggered());
+  EXPECT_EQ(kTestVoltage, voltageCallback.GetLastValue());
+  EXPECT_EQ(kTestVoltage, RoboRioSim::GetUserVoltage6V().value());
+  EXPECT_EQ(kTestVoltage, RobotController::GetVoltage6V());
+
+  RoboRioSim::SetUserCurrent6V(units::ampere_t{kTestCurrent});
+  EXPECT_TRUE(currentCallback.WasTriggered());
+  EXPECT_EQ(kTestCurrent, currentCallback.GetLastValue());
+  EXPECT_EQ(kTestCurrent, RoboRioSim::GetUserCurrent6V().value());
+  EXPECT_EQ(kTestCurrent, RobotController::GetCurrent6V());
+
+  RoboRioSim::SetUserActive6V(false);
+  EXPECT_TRUE(activeCallback.WasTriggered());
+  EXPECT_FALSE(activeCallback.GetLastValue());
+  EXPECT_FALSE(RoboRioSim::GetUserActive6V());
+  EXPECT_FALSE(RobotController::GetEnabled6V());
+
+  RoboRioSim::SetUserFaults6V(kTestFaults);
+  EXPECT_TRUE(faultCallback.WasTriggered());
+  EXPECT_EQ(kTestFaults, faultCallback.GetLastValue());
+  EXPECT_EQ(kTestFaults, RoboRioSim::GetUserFaults6V());
+  EXPECT_EQ(kTestFaults, RobotController::GetFaultCount6V());
+}
+
+TEST(RoboRioSimTest, Set5V) {
+  RoboRioSim::ResetData();
+
+  DoubleCallback voltageCallback;
+  DoubleCallback currentCallback;
+  BooleanCallback activeCallback;
+  IntCallback faultCallback;
+  auto voltageCb = RoboRioSim::RegisterUserVoltage5VCallback(
+      voltageCallback.GetCallback(), false);
+  auto currentCb = RoboRioSim::RegisterUserCurrent5VCallback(
+      currentCallback.GetCallback(), false);
+  auto activeCb = RoboRioSim::RegisterUserActive5VCallback(
+      activeCallback.GetCallback(), false);
+  auto faultsCb = RoboRioSim::RegisterUserFaults5VCallback(
+      faultCallback.GetCallback(), false);
+  constexpr double kTestVoltage = 22.9;
+  constexpr double kTestCurrent = 174;
+  constexpr int kTestFaults = 229;
+
+  RoboRioSim::SetUserVoltage5V(units::volt_t{kTestVoltage});
+  EXPECT_TRUE(voltageCallback.WasTriggered());
+  EXPECT_EQ(kTestVoltage, voltageCallback.GetLastValue());
+  EXPECT_EQ(kTestVoltage, RoboRioSim::GetUserVoltage5V().value());
+  EXPECT_EQ(kTestVoltage, RobotController::GetVoltage5V());
+
+  RoboRioSim::SetUserCurrent5V(units::ampere_t{kTestCurrent});
+  EXPECT_TRUE(currentCallback.WasTriggered());
+  EXPECT_EQ(kTestCurrent, currentCallback.GetLastValue());
+  EXPECT_EQ(kTestCurrent, RoboRioSim::GetUserCurrent5V().value());
+  EXPECT_EQ(kTestCurrent, RobotController::GetCurrent5V());
+
+  RoboRioSim::SetUserActive5V(false);
+  EXPECT_TRUE(activeCallback.WasTriggered());
+  EXPECT_FALSE(activeCallback.GetLastValue());
+  EXPECT_FALSE(RoboRioSim::GetUserActive5V());
+  EXPECT_FALSE(RobotController::GetEnabled5V());
+
+  RoboRioSim::SetUserFaults5V(kTestFaults);
+  EXPECT_TRUE(faultCallback.WasTriggered());
+  EXPECT_EQ(kTestFaults, faultCallback.GetLastValue());
+  EXPECT_EQ(kTestFaults, RoboRioSim::GetUserFaults5V());
+  EXPECT_EQ(kTestFaults, RobotController::GetFaultCount5V());
+}
+
+TEST(RoboRioSimTest, Set3V3) {
+  RoboRioSim::ResetData();
+
+  DoubleCallback voltageCallback;
+  DoubleCallback currentCallback;
+  BooleanCallback activeCallback;
+  IntCallback faultCallback;
+  auto voltageCb = RoboRioSim::RegisterUserVoltage3V3Callback(
+      voltageCallback.GetCallback(), false);
+  auto currentCb = RoboRioSim::RegisterUserCurrent3V3Callback(
+      currentCallback.GetCallback(), false);
+  auto activeCb = RoboRioSim::RegisterUserActive3V3Callback(
+      activeCallback.GetCallback(), false);
+  auto faultsCb = RoboRioSim::RegisterUserFaults3V3Callback(
+      faultCallback.GetCallback(), false);
+  constexpr double kTestVoltage = 22.9;
+  constexpr double kTestCurrent = 174;
+  constexpr int kTestFaults = 229;
+
+  RoboRioSim::SetUserVoltage3V3(units::volt_t{kTestVoltage});
+  EXPECT_TRUE(voltageCallback.WasTriggered());
+  EXPECT_EQ(kTestVoltage, voltageCallback.GetLastValue());
+  EXPECT_EQ(kTestVoltage, RoboRioSim::GetUserVoltage3V3().value());
+  EXPECT_EQ(kTestVoltage, RobotController::GetVoltage3V3());
+
+  RoboRioSim::SetUserCurrent3V3(units::ampere_t{kTestCurrent});
+  EXPECT_TRUE(currentCallback.WasTriggered());
+  EXPECT_EQ(kTestCurrent, currentCallback.GetLastValue());
+  EXPECT_EQ(kTestCurrent, RoboRioSim::GetUserCurrent3V3().value());
+  EXPECT_EQ(kTestCurrent, RobotController::GetCurrent3V3());
+
+  RoboRioSim::SetUserActive3V3(false);
+  EXPECT_TRUE(activeCallback.WasTriggered());
+  EXPECT_FALSE(activeCallback.GetLastValue());
+  EXPECT_FALSE(RoboRioSim::GetUserActive3V3());
+  EXPECT_FALSE(RobotController::GetEnabled3V3());
+
+  RoboRioSim::SetUserFaults3V3(kTestFaults);
+  EXPECT_TRUE(faultCallback.WasTriggered());
+  EXPECT_EQ(kTestFaults, faultCallback.GetLastValue());
+  EXPECT_EQ(kTestFaults, RoboRioSim::GetUserFaults3V3());
+  EXPECT_EQ(kTestFaults, RobotController::GetFaultCount3V3());
+}
+
+}  // namespace frc::sim
diff --git a/wpilibc/src/test/native/cpp/simulation/SimDeviceSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/SimDeviceSimTest.cpp
index ea7b809..5ef2675 100644
--- a/wpilibc/src/test/native/cpp/simulation/SimDeviceSimTest.cpp
+++ b/wpilibc/src/test/native/cpp/simulation/SimDeviceSimTest.cpp
@@ -1,19 +1,17 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <string_view>
 
 #include <hal/SimDevice.h>
-#include <wpi/StringRef.h>
 
 #include "frc/simulation/SimDeviceSim.h"
 #include "gtest/gtest.h"
 
 using namespace frc::sim;
 
-TEST(SimDeviceSimTests, TestBasic) {
+TEST(SimDeviceSimTest, Basic) {
   hal::SimDevice dev{"test"};
   hal::SimBoolean devBool = dev.CreateBoolean("bool", false, false);
 
@@ -24,13 +22,15 @@
   EXPECT_TRUE(devBool.Get());
 }
 
-TEST(SimDeviceSimTests, TestEnumerateDevices) {
+TEST(SimDeviceSimTest, EnumerateDevices) {
   hal::SimDevice dev{"test"};
 
   bool foundit = false;
   SimDeviceSim::EnumerateDevices(
       "te", [&](const char* name, HAL_SimDeviceHandle handle) {
-        if (wpi::StringRef(name) == "test") foundit = true;
+        if (std::string_view(name) == "test") {
+          foundit = true;
+        }
       });
   EXPECT_TRUE(foundit);
 }
diff --git a/wpilibc/src/test/native/cpp/simulation/SimInitializationTest.cpp b/wpilibc/src/test/native/cpp/simulation/SimInitializationTest.cpp
index 1b86e05..e703912 100644
--- a/wpilibc/src/test/native/cpp/simulation/SimInitializationTest.cpp
+++ b/wpilibc/src/test/native/cpp/simulation/SimInitializationTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <exception>
 
@@ -15,14 +12,14 @@
 #include "frc/simulation/AnalogOutputSim.h"
 #include "frc/simulation/AnalogTriggerSim.h"
 #include "frc/simulation/BuiltInAccelerometerSim.h"
+#include "frc/simulation/CTREPCMSim.h"
 #include "frc/simulation/DIOSim.h"
 #include "frc/simulation/DigitalPWMSim.h"
 #include "frc/simulation/DriverStationSim.h"
 #include "frc/simulation/DutyCycleSim.h"
 #include "frc/simulation/EncoderSim.h"
-#include "frc/simulation/PCMSim.h"
-#include "frc/simulation/PDPSim.h"
 #include "frc/simulation/PWMSim.h"
+#include "frc/simulation/PowerDistributionSim.h"
 #include "frc/simulation/RelaySim.h"
 #include "frc/simulation/RoboRioSim.h"
 #include "frc/simulation/SPIAccelerometerSim.h"
@@ -30,7 +27,7 @@
 
 using namespace frc::sim;
 
-TEST(SimInitializationTests, TestAllInitialize) {
+TEST(SimInitializationTest, AllInitialize) {
   HAL_Initialize(500, 0);
   BuiltInAccelerometerSim biacsim;
   AnalogGyroSim agsim{0};
@@ -43,8 +40,8 @@
   (void)dssim;
   EncoderSim esim = EncoderSim::CreateForIndex(0);
   (void)esim;
-  PCMSim pcmsim{0};
-  PDPSim pdpsim{0};
+  CTREPCMSim pcmsim{0};
+  PowerDistributionSim pdpsim{0};
   PWMSim pwmsim{0};
   RelaySim rsim{0};
   RoboRioSim rrsim;
diff --git a/wpilibc/src/test/native/cpp/simulation/SingleJointedArmSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/SingleJointedArmSimTest.cpp
index a553985..03df12b 100644
--- a/wpilibc/src/test/native/cpp/simulation/SingleJointedArmSimTest.cpp
+++ b/wpilibc/src/test/native/cpp/simulation/SingleJointedArmSimTest.cpp
@@ -1,11 +1,8 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <wpi/math>
+#include <wpi/numbers>
 
 #include "frc/simulation/SingleJointedArmSim.h"
 #include "gtest/gtest.h"
@@ -13,13 +10,13 @@
 TEST(SingleJointedArmTest, Disabled) {
   frc::sim::SingleJointedArmSim sim(frc::DCMotor::Vex775Pro(2), 100, 3_kg_sq_m,
                                     9.5_in, -180_deg, 0_deg, 10_lb, true);
-  sim.SetState(frc::MakeMatrix<2, 1>(0.0, 0.0));
+  sim.SetState(Eigen::Vector<double, 2>{0.0, 0.0});
 
   for (size_t i = 0; i < 12 / 0.02; ++i) {
-    sim.SetInput(frc::MakeMatrix<1, 1>(0.0));
+    sim.SetInput(Eigen::Vector<double, 1>{0.0});
     sim.Update(20_ms);
   }
 
   // The arm should swing down.
-  EXPECT_NEAR(sim.GetAngle().to<double>(), -wpi::math::pi / 2, 0.01);
+  EXPECT_NEAR(sim.GetAngle().value(), -wpi::numbers::pi / 2, 0.01);
 }
diff --git a/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp
index 5766751..e7e0405 100644
--- a/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp
+++ b/wpilibc/src/test/native/cpp/simulation/StateSpaceSimTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 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.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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 <iostream>
 
@@ -11,10 +8,10 @@
 #include <units/angular_velocity.h>
 
 #include "frc/Encoder.h"
-#include "frc/PWMVictorSPX.h"
 #include "frc/RobotController.h"
 #include "frc/controller/PIDController.h"
 #include "frc/controller/SimpleMotorFeedforward.h"
+#include "frc/motorcontrol/PWMVictorSPX.h"
 #include "frc/simulation/BatterySim.h"
 #include "frc/simulation/DifferentialDrivetrainSim.h"
 #include "frc/simulation/ElevatorSim.h"
@@ -27,7 +24,7 @@
 #include "frc/system/plant/LinearSystemId.h"
 #include "gtest/gtest.h"
 
-TEST(StateSpaceSimTest, TestFlywheelSim) {
+TEST(StateSpaceSimTest, FlywheelSim) {
   const frc::LinearSystem<1, 1, 1> plant =
       frc::LinearSystemId::IdentifyVelocitySystem<units::radian>(
           0.02_V / 1_rad_per_s, 0.01_V / 1_rad_per_s_sq);
@@ -39,6 +36,9 @@
   frc::sim::EncoderSim encoderSim{encoder};
   frc::PWMVictorSPX motor{0};
 
+  frc::sim::RoboRioSim::ResetData();
+  encoderSim.ResetData();
+
   for (int i = 0; i < 100; i++) {
     // RobotPeriodic runs first
     auto voltageOut = controller.Calculate(encoder.GetRate(), 200.0);
@@ -48,10 +48,10 @@
     // Then, SimulationPeriodic runs
     frc::sim::RoboRioSim::SetVInVoltage(
         frc::sim::BatterySim::Calculate({sim.GetCurrentDraw()}));
-    sim.SetInput(frc::MakeMatrix<1, 1>(
-        motor.Get() * frc::RobotController::GetInputVoltage()));
+    sim.SetInput(Eigen::Vector<double, 1>{
+        motor.Get() * frc::RobotController::GetInputVoltage()});
     sim.Update(20_ms);
-    encoderSim.SetRate(sim.GetAngularVelocity().to<double>());
+    encoderSim.SetRate(sim.GetAngularVelocity().value());
   }
 
   ASSERT_TRUE(std::abs(200 - encoder.GetRate()) < 0.1);