diff --git a/lib/src/test/java/com/team2813/lib2813/preferences/IsolatedPreferences.java b/lib/src/test/java/com/team2813/lib2813/preferences/IsolatedPreferences.java index cb6ed91b..d183769f 100644 --- a/lib/src/test/java/com/team2813/lib2813/preferences/IsolatedPreferences.java +++ b/lib/src/test/java/com/team2813/lib2813/preferences/IsolatedPreferences.java @@ -40,6 +40,7 @@ protected void before() { prevInstance = Preferences.getNetworkTable().getInstance(); tempInstance = NetworkTableInstance.create(); Preferences.setNetworkTableInstance(tempInstance); + tempInstance.waitForListenerQueue(1); removePreferencesListener(); } @@ -52,7 +53,7 @@ protected void after() { // This works around a race condition in WPILib where a listener registered by Preferences can // be called after the NetworkTableInstance was closed (see // https://github.com/wpilibsuite/allwpilib/issues/8215). - if (!tempInstance.waitForListenerQueue(4)) { + if (!tempInstance.waitForListenerQueue(.4)) { System.err.println( "Timed out waiting for the NetworkTableInstance listener queue to empty (waited 400ms);" + " will not close temporary NetworkTableInstance"); @@ -74,7 +75,7 @@ private static void removePreferencesListener() { NetworkTableListener listener = (NetworkTableListener) listnerField.get(null); listnerField.set(null, null); listener.close(); - } catch (NoSuchFieldException | IllegalAccessException e) { + } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) { } } } diff --git a/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java b/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java index 6da2a7fc..a14ebc9d 100644 --- a/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java +++ b/lib/src/test/java/com/team2813/lib2813/subsystems/ParameterizedIntakeSubsystemTest.java @@ -1,5 +1,5 @@ /* -Copyright 2025 Prospect Robotics SWENext Club +Copyright 2025-2026 Prospect Robotics SWENext Club Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,16 +22,15 @@ import com.team2813.lib2813.control.Motor; import com.team2813.lib2813.testing.FakeMotor; import com.team2813.lib2813.testing.junit.jupiter.CommandTester; -import com.team2813.lib2813.testing.junit.jupiter.WPILibExtension; +import com.team2813.lib2813.testing.junit.jupiter.InitWPILib; import edu.wpi.first.wpilibj2.command.Command; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.EnumSource; @ParameterizedClass @EnumSource(ControlMode.class) -@ExtendWith(WPILibExtension.class) +@InitWPILib public final class ParameterizedIntakeSubsystemTest { private static class ConcreteParameterizedIntakeSubsystem extends ParameterizedIntakeSubsystem { diff --git a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/CommandTester.java b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/CommandTester.java index 6fbdd517..7eabcf92 100644 --- a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/CommandTester.java +++ b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/CommandTester.java @@ -20,7 +20,7 @@ /** * Allows tests to run commands. * - *

Tests can get an instance by using {@link WPILibExtension}. + *

Tests can get an instance by using {@link InitWPILib}. * * @since 2.0.0 */ diff --git a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILib.java b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILib.java new file mode 100644 index 00000000..8ac614fc --- /dev/null +++ b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILib.java @@ -0,0 +1,59 @@ +/* +Copyright 2026 Prospect Robotics SWENext Club + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package com.team2813.lib2813.testing.junit.jupiter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * JUnit Jupiter annotation used to signal tests that depends on WPILib. + * + *

Also provides a {@link CommandTester} for tests. + * + *

Example use: + * + *

{@code
+ * @InitWPILib
+ * public final class FlightSubsystemTest {
+ *
+ *   @Test
+ *   public void initiallyNotInAir() {
+ *     var flight = new FlightSubsystem();
+ *
+ *     assertThat(flight.inAir()).isFalse();
+ *   }
+ *
+ *   @Test
+ *   public void takesFlight(CommandTester commandTester) {
+ *     var flight = new FlightSubsystem();
+ *     Command takeOff = flight.createTakeOffCommandCommand();
+ *
+ *     commandTester.runUntilComplete(takeOff);
+ *
+ *     assertThat(flight.inAir()).isTrue();
+ *   }
+ * }
+ * }
+ * + * @since 2.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(InitWPILibExtension.class) +public @interface InitWPILib {} diff --git a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtension.java b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILibExtension.java similarity index 82% rename from testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtension.java rename to testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILibExtension.java index 085d27a1..d3f9bcc0 100644 --- a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtension.java +++ b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILibExtension.java @@ -30,39 +30,8 @@ import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; -/** - * JUnit Jupiter extension for testing code that depends on WPILib. - * - *

Also provides a {@link CommandTester} for tests. - * - *

Example use: - * - *

{@code
- * @ExtendWith(WPILibExtension.class)
- * public final class FlightSubsystemTest {
- *
- *   @Test
- *   public void initiallyNotInAir() {
- *     var flight = new FlightSubsystem();
- *
- *     assertThat(flight.inAir()).isFalse();
- *   }
- *
- *   @Test
- *   public void takesFlight(CommandTester commandTester) {
- *     var flight = new FlightSubsystem();
- *     Command takeOff = flight.createTakeOffCommandCommand();
- *
- *     commandTester.runUntilComplete(takeOff);
- *
- *     assertThat(flight.inAir()).isTrue();
- *   }
- * }
- * }
- * - * @since 2.0.0 - */ -public final class WPILibExtension +/** JUnit Jupiter extension for testing code that depends on WPILib. */ +final class InitWPILibExtension implements Extension, AfterAllCallback, AfterEachCallback, diff --git a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstance.java b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstance.java new file mode 100644 index 00000000..96e3e186 --- /dev/null +++ b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstance.java @@ -0,0 +1,59 @@ +/* +Copyright 2026 Prospect Robotics SWENext Club + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package com.team2813.lib2813.testing.junit.jupiter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * JUnit Jupiter annotation used to signal tests that need an isolated NetworkTableInstance. + * + *

Example use: + * + *

{@code
+ * @ProvideUniqueNetworkTableInstance
+ * public final class IntakeTest {
+ *
+ *   @Test
+ *   public void intakeCoral(NetworkTableInstance ntInstance)  {
+ *     // Do something with ntInstance
+ *   }
+ * }
+ * }
+ * + * @since 2.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(ProvideUniqueNetworkTableInstanceExtension.class) +public @interface ProvideUniqueNetworkTableInstance { + + /** + * How long to wait for the listener queue to empty before destroying the temporary network table + * instance. + */ + double waitForListenerQueueSeconds() default 0.6; + + /** + * Whether to call {@link + * edu.wpi.first.wpilibj.Preferences#setNetworkTableInstance(edu.wpi.first.networktables.NetworkTableInstance)} + * with the temporary network table instance before starting each test. + */ + boolean replacePreferencesNetworkTable() default false; +} diff --git a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/IsolatedNetworkTablesExtension.java b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstanceExtension.java similarity index 64% rename from testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/IsolatedNetworkTablesExtension.java rename to testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstanceExtension.java index fde01441..222e4d22 100644 --- a/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/IsolatedNetworkTablesExtension.java +++ b/testing/src/main/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstanceExtension.java @@ -20,6 +20,7 @@ import edu.wpi.first.wpilibj.Preferences; import java.lang.reflect.Field; import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.Extension; import org.junit.jupiter.api.extension.ExtensionContext; @@ -28,37 +29,39 @@ import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.platform.commons.support.AnnotationSupport; -/** - * JUnit Jupiter extension for providing an isolated NetworkTableInstance to tests. - * - *

Example use: - * - *

{@code
- * @ExtendWith(IsolatedNetworkTablesExtension.class)
- * public final class IntakeTest {
- *
- *   @Test
- *   public void intakeCoral(NetworkTableInstance ntInstance)  {
- *     // Do something with ntInstance
- *   }
- * }
- * }
- * - * @since 2.0.0 - */ -public final class IsolatedNetworkTablesExtension - implements Extension, BeforeEachCallback, AfterEachCallback, ParameterResolver { - private static final Namespace NAMESPACE = Namespace.create(IsolatedNetworkTablesExtension.class); +/** JUnit Jupiter extension for providing an isolated NetworkTableInstance to tests. */ +final class ProvideUniqueNetworkTableInstanceExtension + implements Extension, + BeforeAllCallback, + BeforeEachCallback, + AfterEachCallback, + ParameterResolver { + private static final Namespace NAMESPACE = + Namespace.create(ProvideUniqueNetworkTableInstanceExtension.class); private static final StoreKey DATA_KEY = StoreKey.of(Data.class); + private static final StoreKey ANNOTATION_KEY = + StoreKey.of(ProvideUniqueNetworkTableInstance.class); + + @Override + public void beforeAll(ExtensionContext context) { + Store store = context.getStore(NAMESPACE); + ANNOTATION_KEY.put(store, getAnnotation(context)); + } @Override public void beforeEach(ExtensionContext context) { Store store = context.getStore(NAMESPACE); NetworkTableInstance ntInstance = DATA_KEY.getOrComputeIfAbsent(store, Data::create).testInstance; - Preferences.setNetworkTableInstance(ntInstance); - removePreferencesListener(); + + ProvideUniqueNetworkTableInstance annotation = ANNOTATION_KEY.get(store); + if (annotation.replacePreferencesNetworkTable()) { + Preferences.setNetworkTableInstance(ntInstance); + ntInstance.waitForListenerQueue(1); + removePreferencesListener(); + } } @Override @@ -67,17 +70,22 @@ public void afterEach(ExtensionContext context) { Store store = context.getStore(NAMESPACE); Data data = DATA_KEY.remove(store); if (data != null) { - Preferences.setNetworkTableInstance(data.prevInstance); + ProvideUniqueNetworkTableInstance annotation = ANNOTATION_KEY.get(store); + if (!data.prevInstance.equals(Preferences.getNetworkTable().getInstance())) { + Preferences.setNetworkTableInstance(data.prevInstance); + } // Clear out the listener queue before destroying our temporary NetworkTableInstance. // // This works around a race condition in WPILib where a listener registered by Preferences can // be called after the NetworkTableInstance was closed (see // https://github.com/wpilibsuite/allwpilib/issues/8215). - if (!data.testInstance.waitForListenerQueue(.4)) { - System.err.println( - "Timed out waiting for the NetworkTableInstance listener queue to empty (waited 400ms);" - + " will not close temporary NetworkTableInstance"); + double timeout = annotation.waitForListenerQueueSeconds(); + if (!data.testInstance.waitForListenerQueue(timeout)) { + System.err.printf( + "Timed out waiting for the NetworkTableInstance listener queue to empty (waited" + + " %dms); will not close temporary NetworkTableInstance%n", + Math.round(timeout * 1000)); } else { data.testInstance.close(); } @@ -110,6 +118,17 @@ static Data create() { } } + private static ProvideUniqueNetworkTableInstance getAnnotation(ExtensionContext context) { + return AnnotationSupport.findAnnotation( + context.getRequiredTestClass(), + ProvideUniqueNetworkTableInstance.class, + context.getEnclosingTestClasses()) + .orElseThrow( + () -> + new IllegalStateException( + "Could not find an enclosed class annotated with @IsolatedNetworkTables")); + } + /** * Removes the listener installed by {@link * Preferences#setNetworkTableInstance(NetworkTableInstance)}. @@ -123,7 +142,7 @@ private static void removePreferencesListener() { NetworkTableListener listener = (NetworkTableListener) listnerField.get(null); listnerField.set(null, null); listener.close(); - } catch (NoSuchFieldException | IllegalAccessException e) { + } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) { } } } diff --git a/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/ExtensionAssertions.java b/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/ExtensionAssertions.java index bb9cb086..9646cbab 100644 --- a/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/ExtensionAssertions.java +++ b/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/ExtensionAssertions.java @@ -1,5 +1,5 @@ /* -Copyright 2025 Prospect Robotics SWENext Club +Copyright 2025-2026 Prospect Robotics SWENext Club Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,8 +15,11 @@ */ package com.team2813.lib2813.testing.junit.jupiter; +import java.util.List; import org.junit.platform.testkit.engine.EngineExecutionResults; +import org.junit.platform.testkit.engine.Event; import org.junit.platform.testkit.engine.Events; +import org.opentest4j.MultipleFailuresError; /** * A collection of utility methods that support asserting conditions in tests of JUnit Extensions. @@ -28,7 +31,14 @@ final class ExtensionAssertions { * * @param events Events fired during execution of a test plan on the JUnit Platform. */ - public static void assertHasNoFailures(Events events) { + public static void assertHasNoFailures(Events events, String category) { + if (!events.failed().list().isEmpty()) { + List failures = + events.failed().stream().map(Event::toString).map(AssertionError::new).toList(); + throw new MultipleFailuresError( + String.format("Expected no failed events with category '%s'", category), failures); + } + events.assertStatistics( stats -> { stats.skipped(0); @@ -42,8 +52,8 @@ public static void assertHasNoFailures(Events events) { * @param results Results of executing a test plan on the JUnit Platform. */ public static void assertHasNoFailures(EngineExecutionResults results) { - assertHasNoFailures(results.containerEvents()); - assertHasNoFailures(results.testEvents()); + assertHasNoFailures(results.containerEvents(), "container"); + assertHasNoFailures(results.testEvents(), "test"); } private ExtensionAssertions() { diff --git a/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtensionTest.java b/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILibExtensionTest.java similarity index 96% rename from testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtensionTest.java rename to testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILibExtensionTest.java index f35b49d0..249c8323 100644 --- a/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/WPILibExtensionTest.java +++ b/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/InitWPILibExtensionTest.java @@ -1,5 +1,5 @@ /* -Copyright 2025 Prospect Robotics SWENext Club +Copyright 2025-2026 Prospect Robotics SWENext Club Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -33,12 +33,11 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; -/** Tests for {@link WPILibExtension}. */ -public class WPILibExtensionTest { +/** Tests for {@link InitWPILibExtension}. */ +public class InitWPILibExtensionTest { static final Command FAKE_COMMAND = new Command() {}; @@ -49,7 +48,7 @@ static void initializeHal() { } } - @ExtendWith(WPILibExtension.class) + @InitWPILib @Tag("ignore-outside-testkit") @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public static class SampleTest { diff --git a/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/IsolatedNetworkTablesExtensionTest.java b/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstanceExtensionTest.java similarity index 85% rename from testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/IsolatedNetworkTablesExtensionTest.java rename to testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstanceExtensionTest.java index cc0e0665..6c70e6fb 100644 --- a/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/IsolatedNetworkTablesExtensionTest.java +++ b/testing/src/test/java/com/team2813/lib2813/testing/junit/jupiter/ProvideUniqueNetworkTableInstanceExtensionTest.java @@ -25,14 +25,13 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; -/** Tests for {@link IsolatedNetworkTablesExtension}. */ -class IsolatedNetworkTablesExtensionTest { +/** Tests for {@link ProvideUniqueNetworkTableInstanceExtension}. */ +class ProvideUniqueNetworkTableInstanceExtensionTest { - @ExtendWith(IsolatedNetworkTablesExtension.class) + @ProvideUniqueNetworkTableInstance @Tag("ignore-outside-testkit") @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public static class SampleTest { @@ -45,9 +44,9 @@ public void verifyProvidesNetworkTableParameter(NetworkTableInstance ntInstance) } @Test - public void verifyReplacesPreferencesNetworkTableInstance() { + public void verifyDoesNotReplacePreferencesNetworkTableInstanceByDefault() { assertThat(Preferences.getNetworkTable().getInstance().getHandle()) - .isNotEqualTo(NetworkTableInstance.getDefault().getHandle()); + .isEqualTo(NetworkTableInstance.getDefault().getHandle()); } } // end SampleTest diff --git a/vision/src/test/java/com/team2813/lib2813/vision/MultiPhotonPoseEstimatorTest.java b/vision/src/test/java/com/team2813/lib2813/vision/MultiPhotonPoseEstimatorTest.java index 7be00ae9..73f650e6 100644 --- a/vision/src/test/java/com/team2813/lib2813/vision/MultiPhotonPoseEstimatorTest.java +++ b/vision/src/test/java/com/team2813/lib2813/vision/MultiPhotonPoseEstimatorTest.java @@ -17,7 +17,7 @@ import static com.google.common.truth.Truth.assertThat; -import com.team2813.lib2813.testing.junit.jupiter.IsolatedNetworkTablesExtension; +import com.team2813.lib2813.testing.junit.jupiter.ProvideUniqueNetworkTableInstance; import edu.wpi.first.apriltag.AprilTag; import edu.wpi.first.apriltag.AprilTagFieldLayout; import edu.wpi.first.math.geometry.Pose3d; @@ -27,13 +27,12 @@ import edu.wpi.first.math.geometry.Translation3d; import edu.wpi.first.networktables.NetworkTableInstance; import java.util.List; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.photonvision.PhotonPoseEstimator.PoseStrategy; /** Tests for {@link MultiPhotonPoseEstimator}. */ -@ExtendWith(IsolatedNetworkTablesExtension.class) +@ProvideUniqueNetworkTableInstance class MultiPhotonPoseEstimatorTest { private static final double FIELD_LENGTH = 17.548; private static final double FIELD_WIDTH = 8.052; diff --git a/vision/src/test/java/com/team2813/lib2813/vision/TimestampedStructPublisherTest.java b/vision/src/test/java/com/team2813/lib2813/vision/TimestampedStructPublisherTest.java index 4c21d839..5057f937 100644 --- a/vision/src/test/java/com/team2813/lib2813/vision/TimestampedStructPublisherTest.java +++ b/vision/src/test/java/com/team2813/lib2813/vision/TimestampedStructPublisherTest.java @@ -1,5 +1,5 @@ /* -Copyright 2025 Prospect Robotics SWENext Club +Copyright 2025-2026 Prospect Robotics SWENext Club Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,17 +19,16 @@ import static com.team2813.lib2813.vision.TimestampedStructPublisher.DEFAULT_PUBLISHED_VALUE_VALID_MICROS; import static com.team2813.lib2813.vision.TimestampedStructPublisher.EXPECTED_UPDATE_FREQUENCY_MICROS; -import com.team2813.lib2813.testing.junit.jupiter.IsolatedNetworkTablesExtension; +import com.team2813.lib2813.testing.junit.jupiter.ProvideUniqueNetworkTableInstance; import edu.wpi.first.math.geometry.*; import edu.wpi.first.networktables.*; import edu.wpi.first.units.Units; import java.util.*; import java.util.function.Supplier; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; /** Tests for {@link TimestampedStructPublisher}. */ -@ExtendWith(IsolatedNetworkTablesExtension.class) +@ProvideUniqueNetworkTableInstance(waitForListenerQueueSeconds = 0.6) public class TimestampedStructPublisherTest { private static final long MICROSECONDS_PER_SECOND = 1_000_000; private static final Translation2d DEFAULT_VALUE = new Translation2d(28, 13);