From c71dd38d248250390a88e542350b1b23296e589e Mon Sep 17 00:00:00 2001 From: mxtmx Date: Sat, 24 Jan 2026 02:01:36 -0800 Subject: [PATCH 1/2] Add Phases --- .../com/skeletonarmy/marrow/phases/Phase.java | 131 +++++++++ .../marrow/phases/PhaseManager.java | 252 ++++++++++++++++++ .../marrow/phases/PhaseTests.java | 106 ++++++++ 3 files changed, 489 insertions(+) create mode 100644 core/src/main/java/com/skeletonarmy/marrow/phases/Phase.java create mode 100644 core/src/main/java/com/skeletonarmy/marrow/phases/PhaseManager.java create mode 100644 core/src/test/java/com/skeletonarmy/marrow/phases/PhaseTests.java diff --git a/core/src/main/java/com/skeletonarmy/marrow/phases/Phase.java b/core/src/main/java/com/skeletonarmy/marrow/phases/Phase.java new file mode 100644 index 00000000..70e36bf7 --- /dev/null +++ b/core/src/main/java/com/skeletonarmy/marrow/phases/Phase.java @@ -0,0 +1,131 @@ +package com.skeletonarmy.marrow.phases; + +import androidx.annotation.NonNull; + +import java.util.concurrent.TimeUnit; + +/** + * Represents a named time period within a match. + *

+ * Define phases with a name, duration, and optional time unit (defaults to seconds). + *

+ * Examples: + *

+ * Phase auto = new Phase("Autonomous", 30);
+ * Phase quick = new Phase("Quick", 500, TimeUnit.MILLISECONDS);
+ * 
+ * + * @see PhaseManager + */ +public class Phase { + private final String name; + private final double duration; + private final TimeUnit unit; + + /** + * Creates a phase with a duration in seconds. + * + * @param name the phase name + * @param durationSeconds the duration in seconds + */ + public Phase(@NonNull String name, double durationSeconds) { + this(name, durationSeconds, TimeUnit.SECONDS); + } + + /** + * Creates a phase with a duration and time unit. + * + * @param name the phase name + * @param duration the duration value + * @param unit the time unit (SECONDS, MILLISECONDS, NANOSECONDS) + */ + public Phase(@NonNull String name, double duration, @NonNull TimeUnit unit) { + this.name = name; + this.duration = duration; + this.unit = unit; + } + + /** + * Gets the name of this phase. + * + * @return the phase name + */ + @NonNull + public String getName() { + return name; + } + + /** + * Gets the duration in the original time unit. + * + * @return the duration + */ + public double getDuration() { + return duration; + } + + /** + * Gets the time unit of this phase. + * + * @return the time unit + */ + @NonNull + public TimeUnit getUnit() { + return unit; + } + + /** + * Gets the duration in seconds (converted from the original time unit). + * + * @return the duration in seconds + */ + public double getDurationSeconds() { + return convertToSeconds(duration, unit); + } + + @Override + public String toString() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Phase)) return false; + Phase that = (Phase) o; + return name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + /** + * Converts a duration from the given time unit to seconds. + * + * @param duration the duration value + * @param unit the time unit + * @return the duration in seconds + */ + private static double convertToSeconds(double duration, TimeUnit unit) { + switch (unit) { + case NANOSECONDS: + return duration / 1_000_000_000.0; + case MICROSECONDS: + return duration / 1_000_000.0; + case MILLISECONDS: + return duration / 1_000.0; + case SECONDS: + return duration; + case MINUTES: + return duration * 60.0; + case HOURS: + return duration * 3600.0; + case DAYS: + return duration * 86400.0; + default: + return duration; + } + } +} diff --git a/core/src/main/java/com/skeletonarmy/marrow/phases/PhaseManager.java b/core/src/main/java/com/skeletonarmy/marrow/phases/PhaseManager.java new file mode 100644 index 00000000..443aeded --- /dev/null +++ b/core/src/main/java/com/skeletonarmy/marrow/phases/PhaseManager.java @@ -0,0 +1,252 @@ +package com.skeletonarmy.marrow.phases; + +import androidx.annotation.NonNull; +import com.qualcomm.robotcore.eventloop.opmode.OpMode; +import com.qualcomm.robotcore.eventloop.opmode.OpModeManagerImpl; +import com.qualcomm.robotcore.robot.RobotState; +import com.skeletonarmy.marrow.OpModeManager; +import com.skeletonarmy.marrow.TimerEx; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Manages match phases and automatically transitions based on elapsed time. + *

+ * Initialize with phases, then call {@link #update()} each loop iteration. Use + * {@link #getCurrentPhase()} for time-aware logic or {@link #addPhaseListener} for callbacks. + *

+ * Typical usage: + *

+ * PhaseManager.init(this, new Phase("Auto", 30), new Phase("Park", 3));
+ * while (opModeIsActive()) {
+ *     PhaseManager.update();
+ *     if (PhaseManager.isCurrentPhase("Park")) { ... }
+ * }
+ * 
+ * + * @see Phase + */ +public class PhaseManager { + private static OpMode currentOpMode = null; + private static TimerEx matchTimer = null; + + // Configured phases in order + private static final List phases = new ArrayList<>(); + + // Current state + private static int currentPhaseIndex = 0; + private static int previousPhaseIndex = -1; + private static Phase currentPhase = null; + + // Listeners + private static final List phaseListeners = new ArrayList<>(); + + private PhaseManager() {} + + /** + * Initializes the manager with phases in order. + *

+ * Call once before {@code waitForStart()}. + * + * @param opMode the OpMode instance + * @param phasesToRun phases to transition through in order + * @throws IllegalArgumentException if no phases are provided + */ + public static void init(@NonNull OpMode opMode, @NonNull Phase... phasesToRun) { + if (phasesToRun.length == 0) { + throw new IllegalArgumentException("At least one phase must be provided"); + } + + currentOpMode = opMode; + matchTimer = new TimerEx(getTotalDuration(phasesToRun), TimeUnit.SECONDS); + phases.clear(); + phases.addAll(Arrays.asList(phasesToRun)); + currentPhaseIndex = 0; + previousPhaseIndex = -1; + currentPhase = phases.get(0); + } + + /** + * Sums phase durations in seconds. + */ + private static double getTotalDuration(Phase[] phases) { + double total = 0; + for (Phase phase : phases) { + total += phase.getDurationSeconds(); + } + return total; + } + + /** + * Updates the current phase based on elapsed time. Call once per loop. + */ + public static void update() { + if (currentOpMode == null || phases.isEmpty()) { + return; + } + + // Start the timer on first update (when match starts) + if (!matchTimer.isOn()) { + try { + OpModeManagerImpl manager = OpModeManager.getManager(); + RobotState state = manager.getRobotState(); + if (state == RobotState.RUNNING) { + matchTimer.start(); + } + } catch (Exception e) { + // OpModeManager not available, match hasn't started yet + return; + } + } + + // Calculate elapsed time from match start + double elapsedSeconds = matchTimer.getElapsed(); + + // Find which phase we're in based on elapsed time + double timeAccumulated = 0; + for (int i = 0; i < phases.size(); i++) { + Phase phase = phases.get(i); + double phaseEnd = timeAccumulated + phase.getDurationSeconds(); + + if (elapsedSeconds < phaseEnd || i == phases.size() - 1) { + // We're in this phase (or stay on last phase if exceeded) + currentPhaseIndex = i; + currentPhase = phase; + break; + } + + timeAccumulated = phaseEnd; + } + + // Notify listeners of phase transitions + if (currentPhaseIndex != previousPhaseIndex && previousPhaseIndex != -1) { + notifyPhaseChange(); + } + previousPhaseIndex = currentPhaseIndex; + } + + /** + * Gets the current phase. + */ + public static @NonNull Phase getCurrentPhase() { + return currentPhase != null ? currentPhase : phases.get(0); + } + + /** + * Checks if the current phase matches the given phase (by name). + */ + public static boolean isCurrentPhase(@NonNull Phase phase) { + return getCurrentPhase().equals(phase); + } + + /** + * Checks if the current phase name matches the given name. + */ + public static boolean isCurrentPhase(@NonNull String phaseName) { + return getCurrentPhase().getName().equals(phaseName); + } + + /** + * Gets elapsed time in seconds since the match started (0 if not started). + */ + public static double getElapsedTime() { + return matchTimer != null ? matchTimer.getElapsed() : 0; + } + + /** + * Gets remaining time in the match (in seconds). + */ + public static double getTimeRemaining() { + if (matchTimer == null) { + return getTotalMatchDuration(); + } + + double remaining = matchTimer.getRemaining(); + return Math.max(0, remaining); + } + + /** + * Gets remaining time in the current phase (in seconds). + */ + public static double getPhaseTimeRemaining() { + if (currentPhase == null || !matchTimer.isOn()) { + return currentPhase != null ? currentPhase.getDurationSeconds() : 0; + } + + double phaseStartTime = getPhaseStartTime(); + double elapsedInPhase = getElapsedTime() - phaseStartTime; + return Math.max(0, currentPhase.getDurationSeconds() - elapsedInPhase); + } + + /** + * Calculates when the current phase started (elapsed seconds from match start). + */ + private static double getPhaseStartTime() { + double start = 0; + for (int i = 0; i < currentPhaseIndex; i++) { + start += phases.get(i).getDurationSeconds(); + } + return start; + } + + /** + * Gets total match duration (sum of all phase durations, in seconds). + */ + public static double getTotalMatchDuration() { + double total = 0; + for (Phase phase : phases) { + total += phase.getDurationSeconds(); + } + return total; + } + + /** + * Registers a listener to be notified on phase changes. + */ + public static void addPhaseListener(@NonNull PhaseListener listener) { + phaseListeners.add(listener); + } + + /** + * Unregisters a phase listener. + */ + public static void removePhaseListener(@NonNull PhaseListener listener) { + phaseListeners.remove(listener); + } + + /** + * Clears all phase listeners. + */ + public static void clearPhaseListeners() { + phaseListeners.clear(); + } + + private static void notifyPhaseChange() { + for (PhaseListener listener : phaseListeners) { + try { + listener.onPhaseEntered(currentPhase); + } catch (Exception e) { + // Prevent listener exceptions from breaking the match + if (currentOpMode != null) { + currentOpMode.telemetry.addLine("ERROR in phase listener: " + e.getMessage()); + } + } + } + } + + /** + * Callback for phase transitions. + *

+ * Example: {@code addPhaseListener(phase -> { if ("Endgame".equals(phase.getName())) {...} })} + */ + @FunctionalInterface + public interface PhaseListener { + /** + * Called when entering a new phase. + */ + void onPhaseEntered(@NonNull Phase newPhase); + } +} diff --git a/core/src/test/java/com/skeletonarmy/marrow/phases/PhaseTests.java b/core/src/test/java/com/skeletonarmy/marrow/phases/PhaseTests.java new file mode 100644 index 00000000..11a404e0 --- /dev/null +++ b/core/src/test/java/com/skeletonarmy/marrow/phases/PhaseTests.java @@ -0,0 +1,106 @@ +package com.skeletonarmy.marrow.phases; + +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.*; + +public class PhaseTests { + + @Test + public void matchPhase_creation_withNameAndDuration() { + Phase phase = new Phase("Autonomous", 30); + assertEquals("Should have correct name", "Autonomous", phase.getName()); + assertEquals("Should have correct duration", 30.0, phase.getDurationSeconds(), 0.01); + } + + @Test + public void matchPhase_toString_returnsName() { + Phase phase = new Phase("Teleop", 150); + assertEquals("toString should return name", "Teleop", phase.toString()); + } + + @Test + public void matchPhase_equality_sameNameAreEqual() { + Phase phase1 = new Phase("Autonomous", 30); + Phase phase2 = new Phase("Autonomous", 45); + assertEquals("Phases with same name should be equal", phase1, phase2); + } + + @Test + public void matchPhase_equality_differentNamesAreNotEqual() { + Phase phase1 = new Phase("Autonomous", 30); + Phase phase2 = new Phase("Teleop", 30); + assertNotEquals("Phases with different names should not be equal", phase1, phase2); + } + + @Test + public void matchPhase_hashCode_sameForSameNames() { + Phase phase1 = new Phase("Autonomous", 30); + Phase phase2 = new Phase("Autonomous", 45); + assertEquals("Hash codes should match for same name", phase1.hashCode(), phase2.hashCode()); + } + + @Test + public void matchPhase_variableDuration() { + Phase shortPhase = new Phase("Short", 5); + Phase longPhase = new Phase("Long", 120.5); + + assertEquals("Short phase should be 5 seconds", 5.0, shortPhase.getDurationSeconds(), 0.01); + assertEquals("Long phase should be 120.5 seconds", 120.5, longPhase.getDurationSeconds(), 0.01); + } + + @Test + public void matchPhase_canCreateMultiplePhases() { + Phase auto = new Phase("Autonomous", 30); + Phase teleop = new Phase("Teleop", 120); + Phase endgame = new Phase("Endgame", 30); + + assertEquals("Auto", "Autonomous", auto.getName()); + assertEquals("Teleop", "Teleop", teleop.getName()); + assertEquals("Endgame", "Endgame", endgame.getName()); + } + + @Test + public void matchPhase_defaultTimeUnitIsSeconds() { + Phase phase = new Phase("Test", 30); + assertEquals("Default unit should be SECONDS", TimeUnit.SECONDS, phase.getUnit()); + } + + @Test + public void matchPhase_withMilliseconds() { + Phase phase = new Phase("Quick", 5000, TimeUnit.MILLISECONDS); + assertEquals("Should have correct duration", 5000, phase.getDuration(), 0.01); + assertEquals("Should have correct unit", TimeUnit.MILLISECONDS, phase.getUnit()); + assertEquals("Should convert to 5 seconds", 5.0, phase.getDurationSeconds(), 0.01); + } + + @Test + public void matchPhase_withNanoseconds() { + Phase phase = new Phase("Precise", 1_000_000_000, TimeUnit.NANOSECONDS); + assertEquals("Should have correct duration", 1_000_000_000, phase.getDuration(), 0.01); + assertEquals("Should have correct unit", TimeUnit.NANOSECONDS, phase.getUnit()); + assertEquals("Should convert to 1 second", 1.0, phase.getDurationSeconds(), 0.01); + } + + @Test + public void matchPhase_mixedTimeUnits() { + Phase secondsPhase = new Phase("Seconds", 10); + Phase millisPhase = new Phase("Millis", 10_000, TimeUnit.MILLISECONDS); + Phase nanosPhase = new Phase("Nanos", 10_000_000_000L, TimeUnit.NANOSECONDS); + + assertEquals("Seconds phase", 10.0, secondsPhase.getDurationSeconds(), 0.01); + assertEquals("Millis phase", 10.0, millisPhase.getDurationSeconds(), 0.01); + assertEquals("Nanos phase", 10.0, nanosPhase.getDurationSeconds(), 0.01); + } + + @Test + public void matchPhase_fractionalDurations() { + Phase fractionalSeconds = new Phase("Partial", 2.5); + Phase fractionalMillis = new Phase("PartialMs", 2500, TimeUnit.MILLISECONDS); + + assertEquals("Fractional seconds", 2.5, fractionalSeconds.getDurationSeconds(), 0.01); + assertEquals("Fractional millis", 2.5, fractionalMillis.getDurationSeconds(), 0.01); + } +} From 03ca9e3ea264b3d4454b284d60acb86d672c972d Mon Sep 17 00:00:00 2001 From: mxtmx Date: Tue, 27 Jan 2026 16:48:08 -0800 Subject: [PATCH 2/2] Refactor PhaseManager to instance-based design PhaseManager is now instance-based instead of static, improving encapsulation and usability. Equality and hashCode methods were removed from Phase, so phase equality is now by reference. Corresponding tests were updated to reflect these changes. --- .../com/skeletonarmy/marrow/phases/Phase.java | 13 --- .../marrow/phases/PhaseManager.java | 110 +++++++++--------- .../marrow/phases/PhaseTests.java | 34 +++--- 3 files changed, 67 insertions(+), 90 deletions(-) diff --git a/core/src/main/java/com/skeletonarmy/marrow/phases/Phase.java b/core/src/main/java/com/skeletonarmy/marrow/phases/Phase.java index 70e36bf7..7d2a3c8e 100644 --- a/core/src/main/java/com/skeletonarmy/marrow/phases/Phase.java +++ b/core/src/main/java/com/skeletonarmy/marrow/phases/Phase.java @@ -88,19 +88,6 @@ public String toString() { return name; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Phase)) return false; - Phase that = (Phase) o; - return name.equals(that.name); - } - - @Override - public int hashCode() { - return name.hashCode(); - } - /** * Converts a duration from the given time unit to seconds. * diff --git a/core/src/main/java/com/skeletonarmy/marrow/phases/PhaseManager.java b/core/src/main/java/com/skeletonarmy/marrow/phases/PhaseManager.java index 443aeded..cba9442a 100644 --- a/core/src/main/java/com/skeletonarmy/marrow/phases/PhaseManager.java +++ b/core/src/main/java/com/skeletonarmy/marrow/phases/PhaseManager.java @@ -15,39 +15,36 @@ /** * Manages match phases and automatically transitions based on elapsed time. *

- * Initialize with phases, then call {@link #update()} each loop iteration. Use + * Create an instance with phases, then call {@link #update()} each loop iteration. Use * {@link #getCurrentPhase()} for time-aware logic or {@link #addPhaseListener} for callbacks. *

* Typical usage: *

- * PhaseManager.init(this, new Phase("Auto", 30), new Phase("Park", 3));
+ * PhaseManager manager = new PhaseManager(this, new Phase("Auto", 30), new Phase("Park", 3));
  * while (opModeIsActive()) {
- *     PhaseManager.update();
- *     if (PhaseManager.isCurrentPhase("Park")) { ... }
+ *     manager.update();
+ *     if (manager.isCurrentPhase("Park")) { ... }
  * }
  * 
* * @see Phase */ public class PhaseManager { - private static OpMode currentOpMode = null; - private static TimerEx matchTimer = null; + private final OpMode opMode; + private final TimerEx matchTimer; // Configured phases in order - private static final List phases = new ArrayList<>(); + private final List phases; // Current state - private static int currentPhaseIndex = 0; - private static int previousPhaseIndex = -1; - private static Phase currentPhase = null; + private Phase currentPhase; + private Phase previousPhase; // Listeners - private static final List phaseListeners = new ArrayList<>(); - - private PhaseManager() {} + private final List phaseListeners; /** - * Initializes the manager with phases in order. + * Creates a phase manager with phases in order. *

* Call once before {@code waitForStart()}. * @@ -55,24 +52,23 @@ private PhaseManager() {} * @param phasesToRun phases to transition through in order * @throws IllegalArgumentException if no phases are provided */ - public static void init(@NonNull OpMode opMode, @NonNull Phase... phasesToRun) { + public PhaseManager(@NonNull OpMode opMode, @NonNull Phase... phasesToRun) { if (phasesToRun.length == 0) { throw new IllegalArgumentException("At least one phase must be provided"); } - currentOpMode = opMode; - matchTimer = new TimerEx(getTotalDuration(phasesToRun), TimeUnit.SECONDS); - phases.clear(); - phases.addAll(Arrays.asList(phasesToRun)); - currentPhaseIndex = 0; - previousPhaseIndex = -1; - currentPhase = phases.get(0); + this.opMode = opMode; + this.phases = new ArrayList<>(Arrays.asList(phasesToRun)); + this.matchTimer = new TimerEx(getTotalDuration(phasesToRun), TimeUnit.SECONDS); + this.currentPhase = phases.get(0); + this.previousPhase = null; + this.phaseListeners = new ArrayList<>(); } /** * Sums phase durations in seconds. */ - private static double getTotalDuration(Phase[] phases) { + private static double getTotalDuration(@NonNull Phase[] phases) { double total = 0; for (Phase phase : phases) { total += phase.getDurationSeconds(); @@ -83,8 +79,8 @@ private static double getTotalDuration(Phase[] phases) { /** * Updates the current phase based on elapsed time. Call once per loop. */ - public static void update() { - if (currentOpMode == null || phases.isEmpty()) { + public void update() { + if (phases.isEmpty()) { return; } @@ -107,63 +103,64 @@ public static void update() { // Find which phase we're in based on elapsed time double timeAccumulated = 0; - for (int i = 0; i < phases.size(); i++) { - Phase phase = phases.get(i); + Phase newPhase = phases.get(0); + for (Phase phase : phases) { double phaseEnd = timeAccumulated + phase.getDurationSeconds(); - if (elapsedSeconds < phaseEnd || i == phases.size() - 1) { - // We're in this phase (or stay on last phase if exceeded) - currentPhaseIndex = i; - currentPhase = phase; + if (elapsedSeconds < phaseEnd) { + newPhase = phase; break; } timeAccumulated = phaseEnd; + newPhase = phase; // Stay on last phase if exceeded } // Notify listeners of phase transitions - if (currentPhaseIndex != previousPhaseIndex && previousPhaseIndex != -1) { + if (!newPhase.equals(currentPhase) && previousPhase != null) { + previousPhase = currentPhase; + currentPhase = newPhase; notifyPhaseChange(); + } else if (previousPhase == null) { + previousPhase = currentPhase; + currentPhase = newPhase; + } else { + currentPhase = newPhase; } - previousPhaseIndex = currentPhaseIndex; } /** * Gets the current phase. */ - public static @NonNull Phase getCurrentPhase() { + public @NonNull Phase getCurrentPhase() { return currentPhase != null ? currentPhase : phases.get(0); } /** - * Checks if the current phase matches the given phase (by name). + * Checks if the current phase matches the given phase (by reference). */ - public static boolean isCurrentPhase(@NonNull Phase phase) { + public boolean isCurrentPhase(@NonNull Phase phase) { return getCurrentPhase().equals(phase); } /** * Checks if the current phase name matches the given name. */ - public static boolean isCurrentPhase(@NonNull String phaseName) { + public boolean isCurrentPhase(@NonNull String phaseName) { return getCurrentPhase().getName().equals(phaseName); } /** * Gets elapsed time in seconds since the match started (0 if not started). */ - public static double getElapsedTime() { - return matchTimer != null ? matchTimer.getElapsed() : 0; + public double getElapsedTime() { + return matchTimer.getElapsed(); } /** * Gets remaining time in the match (in seconds). */ - public static double getTimeRemaining() { - if (matchTimer == null) { - return getTotalMatchDuration(); - } - + public double getTimeRemaining() { double remaining = matchTimer.getRemaining(); return Math.max(0, remaining); } @@ -171,7 +168,7 @@ public static double getTimeRemaining() { /** * Gets remaining time in the current phase (in seconds). */ - public static double getPhaseTimeRemaining() { + public double getPhaseTimeRemaining() { if (currentPhase == null || !matchTimer.isOn()) { return currentPhase != null ? currentPhase.getDurationSeconds() : 0; } @@ -184,10 +181,13 @@ public static double getPhaseTimeRemaining() { /** * Calculates when the current phase started (elapsed seconds from match start). */ - private static double getPhaseStartTime() { + private double getPhaseStartTime() { double start = 0; - for (int i = 0; i < currentPhaseIndex; i++) { - start += phases.get(i).getDurationSeconds(); + for (Phase phase : phases) { + if (phase.equals(currentPhase)) { + break; + } + start += phase.getDurationSeconds(); } return start; } @@ -195,7 +195,7 @@ private static double getPhaseStartTime() { /** * Gets total match duration (sum of all phase durations, in seconds). */ - public static double getTotalMatchDuration() { + public double getTotalMatchDuration() { double total = 0; for (Phase phase : phases) { total += phase.getDurationSeconds(); @@ -206,33 +206,31 @@ public static double getTotalMatchDuration() { /** * Registers a listener to be notified on phase changes. */ - public static void addPhaseListener(@NonNull PhaseListener listener) { + public void addPhaseListener(@NonNull PhaseListener listener) { phaseListeners.add(listener); } /** * Unregisters a phase listener. */ - public static void removePhaseListener(@NonNull PhaseListener listener) { + public void removePhaseListener(@NonNull PhaseListener listener) { phaseListeners.remove(listener); } /** * Clears all phase listeners. */ - public static void clearPhaseListeners() { + public void clearPhaseListeners() { phaseListeners.clear(); } - private static void notifyPhaseChange() { + private void notifyPhaseChange() { for (PhaseListener listener : phaseListeners) { try { listener.onPhaseEntered(currentPhase); } catch (Exception e) { // Prevent listener exceptions from breaking the match - if (currentOpMode != null) { - currentOpMode.telemetry.addLine("ERROR in phase listener: " + e.getMessage()); - } + opMode.telemetry.addLine("ERROR in phase listener: " + e.getMessage()); } } } diff --git a/core/src/test/java/com/skeletonarmy/marrow/phases/PhaseTests.java b/core/src/test/java/com/skeletonarmy/marrow/phases/PhaseTests.java index 11a404e0..d1c6576e 100644 --- a/core/src/test/java/com/skeletonarmy/marrow/phases/PhaseTests.java +++ b/core/src/test/java/com/skeletonarmy/marrow/phases/PhaseTests.java @@ -1,5 +1,6 @@ package com.skeletonarmy.marrow.phases; +import org.junit.Before; import org.junit.Test; import java.util.concurrent.TimeUnit; @@ -7,6 +8,9 @@ import static org.junit.Assert.*; public class PhaseTests { + private Phase autonomousPhase; + private Phase teleopPhase; + private Phase endgamePhase; @Test public void matchPhase_creation_withNameAndDuration() { @@ -22,24 +26,16 @@ public void matchPhase_toString_returnsName() { } @Test - public void matchPhase_equality_sameNameAreEqual() { - Phase phase1 = new Phase("Autonomous", 30); - Phase phase2 = new Phase("Autonomous", 45); - assertEquals("Phases with same name should be equal", phase1, phase2); - } - - @Test - public void matchPhase_equality_differentNamesAreNotEqual() { - Phase phase1 = new Phase("Autonomous", 30); - Phase phase2 = new Phase("Teleop", 30); - assertNotEquals("Phases with different names should not be equal", phase1, phase2); + public void matchPhase_equality_sameObjectAreEqual() { + Phase phase = new Phase("Autonomous", 30); + assertEquals("Same object should be equal to itself", phase, phase); } @Test - public void matchPhase_hashCode_sameForSameNames() { + public void matchPhase_equality_differentObjectsAreNotEqual() { Phase phase1 = new Phase("Autonomous", 30); - Phase phase2 = new Phase("Autonomous", 45); - assertEquals("Hash codes should match for same name", phase1.hashCode(), phase2.hashCode()); + Phase phase2 = new Phase("Autonomous", 30); + assertNotEquals("Different objects should not be equal even with same name", phase1, phase2); } @Test @@ -53,13 +49,9 @@ public void matchPhase_variableDuration() { @Test public void matchPhase_canCreateMultiplePhases() { - Phase auto = new Phase("Autonomous", 30); - Phase teleop = new Phase("Teleop", 120); - Phase endgame = new Phase("Endgame", 30); - - assertEquals("Auto", "Autonomous", auto.getName()); - assertEquals("Teleop", "Teleop", teleop.getName()); - assertEquals("Endgame", "Endgame", endgame.getName()); + assertEquals("Auto", "Autonomous", autonomousPhase.getName()); + assertEquals("Teleop", "Teleop", teleopPhase.getName()); + assertEquals("Endgame", "Endgame", endgamePhase.getName()); } @Test