diff --git a/wpilibc/src/main/native/cpp/Timer.cpp b/wpilibc/src/main/native/cpp/Timer.cpp index 62a05ee2d2..f5bdb93125 100644 --- a/wpilibc/src/main/native/cpp/Timer.cpp +++ b/wpilibc/src/main/native/cpp/Timer.cpp @@ -63,7 +63,7 @@ void Timer::Stop() { } bool Timer::HasElapsed(units::second_t period) const { - return Get() > period; + return Get() >= period; } bool Timer::HasPeriodPassed(units::second_t period) { @@ -71,7 +71,7 @@ bool Timer::HasPeriodPassed(units::second_t period) { } bool Timer::AdvanceIfElapsed(units::second_t period) { - if (Get() > period) { + if (Get() >= period) { // Advance the start time by the period. m_startTime += period; // Don't set it to the current time... we want to avoid drift. diff --git a/wpilibc/src/test/native/cpp/TimerTest.cpp b/wpilibc/src/test/native/cpp/TimerTest.cpp new file mode 100644 index 0000000000..a7adc45b42 --- /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/wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java index 17e1164c38..cead2ce4d0 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/Timer.java @@ -123,7 +123,7 @@ public class Timer { */ public boolean hasElapsed(double seconds) { synchronized (m_lock) { - return get() > seconds; + return get() >= seconds; } } @@ -151,7 +151,7 @@ public class Timer { */ public boolean advanceIfElapsed(double seconds) { synchronized (m_lock) { - if (get() > seconds) { + if (get() >= seconds) { // Advance the start time by the period. // Don't set it to the current time... we want to avoid drift. m_startTime += seconds * 1000; diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/TimerTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/TimerTest.java new file mode 100644 index 0000000000..89619dd633 --- /dev/null +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/TimerTest.java @@ -0,0 +1,132 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.simulation.SimHooks; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.ResourceLock; + +class TimerTest { + @BeforeEach + void setup() { + HAL.initialize(500, 0); + SimHooks.pauseTiming(); + SimHooks.restartTiming(); + } + + @AfterEach + void cleanup() { + SimHooks.resumeTiming(); + } + + @Test + @ResourceLock("timing") + void startStopTest() { + var timer = new Timer(); + + // Verify timer is initialized as stopped + assertEquals(timer.get(), 0.0); + SimHooks.stepTiming(0.5); + assertEquals(timer.get(), 0.0); + + // Verify timer increments after it's started + timer.start(); + SimHooks.stepTiming(0.5); + assertEquals(timer.get(), 0.5, 1e-9); + + // Verify timer stops incrementing after it's stopped + timer.stop(); + SimHooks.stepTiming(0.5); + assertEquals(timer.get(), 0.5, 1e-9); + } + + @Test + @ResourceLock("timing") + void resetTest() { + var timer = new Timer(); + timer.start(); + + // Advance timer to 500 ms + assertEquals(timer.get(), 0.0); + SimHooks.stepTiming(0.5); + assertEquals(timer.get(), 0.5, 1e-9); + + // Verify timer reports 0 ms after reset + timer.reset(); + assertEquals(timer.get(), 0.0); + + // Verify timer continues incrementing + SimHooks.stepTiming(0.5); + assertEquals(timer.get(), 0.5, 1e-9); + + // Verify timer doesn't start incrementing after reset if it was stopped + timer.stop(); + timer.reset(); + SimHooks.stepTiming(0.5); + assertEquals(timer.get(), 0.0); + } + + @Test + @ResourceLock("timing") + void hasElapsedTest() { + var timer = new Timer(); + + // Verify 0 ms has elapsed since timer hasn't started + assertTrue(timer.hasElapsed(0.0)); + + // Verify timer doesn't report elapsed time when stopped + SimHooks.stepTiming(0.5); + assertFalse(timer.hasElapsed(0.4)); + + timer.start(); + + // Verify timer reports >= 400 ms has elapsed after multiple calls + SimHooks.stepTiming(0.5); + assertTrue(timer.hasElapsed(0.4)); + assertTrue(timer.hasElapsed(0.4)); + } + + @Test + @ResourceLock("timing") + void advanceIfElapsedTest() { + var timer = new Timer(); + + // Verify 0 ms has elapsed since timer hasn't started + assertTrue(timer.advanceIfElapsed(0.0)); + + // Verify timer doesn't report elapsed time when stopped + SimHooks.stepTiming(0.5); + assertFalse(timer.advanceIfElapsed(0.4)); + + timer.start(); + + // Verify timer reports >= 400 ms has elapsed for only first call + SimHooks.stepTiming(0.5); + assertTrue(timer.advanceIfElapsed(0.4)); + assertFalse(timer.advanceIfElapsed(0.4)); + + // Verify timer reports >= 400 ms has elapsed for two calls + SimHooks.stepTiming(1.0); + assertTrue(timer.advanceIfElapsed(0.4)); + assertTrue(timer.advanceIfElapsed(0.4)); + assertFalse(timer.advanceIfElapsed(0.4)); + } + + @Test + @ResourceLock("timing") + void getFPGATimestampTest() { + double start = Timer.getFPGATimestamp(); + SimHooks.stepTiming(0.5); + double end = Timer.getFPGATimestamp(); + assertEquals(start + 0.5, end, 1e-9); + } +}