diff --git a/framework/tst-self/dslabs/framework/testing/utils/CloningTest.java b/framework/tst-self/dslabs/framework/testing/utils/CloningTest.java index f8c124cb..49516f17 100644 --- a/framework/tst-self/dslabs/framework/testing/utils/CloningTest.java +++ b/framework/tst-self/dslabs/framework/testing/utils/CloningTest.java @@ -416,17 +416,13 @@ public void serializableTraces() throws InvocationTargetException, InstantiationException, IllegalAccessException { List es = new ArrayList<>(); es.add( - new Event( - new MessageEnvelope( - new LocalAddress("foo"), - new AddressExample("bar"), - new MessageExample("asdf", false)))); + new MessageEnvelope( + new LocalAddress("foo"), new AddressExample("bar"), new MessageExample("asdf", false))); es.add( - new Event( - new MessageEnvelope( - new LocalAddress("asdf"), - new AddressExample("1234"), - new MessageExample("baz", false)))); + new MessageEnvelope( + new LocalAddress("asdf"), + new AddressExample("1234"), + new MessageExample("baz", false))); var con = SerializableTrace.class.getDeclaredConstructors()[0]; con.setAccessible(true); diff --git a/framework/tst/dslabs/framework/testing/Event.java b/framework/tst/dslabs/framework/testing/Event.java index 6d6d00fa..fac52cb0 100644 --- a/framework/tst/dslabs/framework/testing/Event.java +++ b/framework/tst/dslabs/framework/testing/Event.java @@ -24,8 +24,6 @@ import dslabs.framework.Address; import java.io.Serializable; -import lombok.EqualsAndHashCode; -import lombok.Getter; /** * Represents either a timer to fire or a message to deliver. @@ -33,45 +31,14 @@ * @see dslabs.framework.testing.search.SearchState * @see dslabs.framework.testing.runner.RunState */ -@Getter -@EqualsAndHashCode -public class Event implements Serializable { - // invariant: exactly one of the {message, timer} fields is null - private final MessageEnvelope message; - private final TimerEnvelope timer; +public sealed interface Event extends Serializable permits MessageEnvelope, TimerEnvelope { + Address locationRootAddress(); - public Event(MessageEnvelope messageEnvelope) { - this.message = messageEnvelope; - this.timer = null; + default boolean isMessage() { + return this instanceof MessageEnvelope; } - public Event(TimerEnvelope timer) { - this.message = null; - this.timer = timer; - } - - public boolean isMessage() { - return message != null; - } - - public boolean isTimer() { - return timer != null; - } - - public Address locationRootAddress() { - if (isMessage()) { - return message.to().rootAddress(); - } else { - return timer.to().rootAddress(); - } - } - - @Override - public String toString() { - if (isMessage()) { - return message.toString(); - } else { - return timer.toString(); - } + default boolean isTimer() { + return this instanceof TimerEnvelope; } } diff --git a/framework/tst/dslabs/framework/testing/MessageEnvelope.java b/framework/tst/dslabs/framework/testing/MessageEnvelope.java index 7eaf0717..bf749c6b 100644 --- a/framework/tst/dslabs/framework/testing/MessageEnvelope.java +++ b/framework/tst/dslabs/framework/testing/MessageEnvelope.java @@ -24,22 +24,16 @@ import dslabs.framework.Address; import dslabs.framework.Message; -import java.io.Serializable; -import lombok.Data; +import org.checkerframework.checker.nullness.qual.NonNull; -@Data -public final class MessageEnvelope implements Serializable { - private final Address from, to; - private final Message message; - - public MessageEnvelope(Address from, Address to, Message message) { - this.from = from; - this.to = to; - this.message = message; +public record MessageEnvelope(Address from, Address to, Message message) implements Event { + @Override + public Address locationRootAddress() { + return to.rootAddress(); } @Override - public String toString() { + public @NonNull String toString() { return String.format("Message(%s -> %s, %s)", from, to, message); } } diff --git a/framework/tst/dslabs/framework/testing/TimerEnvelope.java b/framework/tst/dslabs/framework/testing/TimerEnvelope.java index 9a77253f..142fa609 100644 --- a/framework/tst/dslabs/framework/testing/TimerEnvelope.java +++ b/framework/tst/dslabs/framework/testing/TimerEnvelope.java @@ -25,10 +25,10 @@ import dslabs.framework.Address; import dslabs.framework.Timer; import dslabs.framework.VizIgnore; -import java.io.Serializable; import java.util.Random; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NonNull; /** * Stores a timer, its delivery address, its duration, and its creation time. Equality is based on @@ -38,7 +38,7 @@ */ @Data @EqualsAndHashCode(of = {"to", "timer", "minTimerLengthMillis", "maxTimerLengthMillis"}) -public final class TimerEnvelope implements Serializable, Comparable { +public final class TimerEnvelope implements Event, Comparable { private static final Random rand = new Random(); private final Address to; @@ -69,6 +69,11 @@ public TimerEnvelope( this.startTimeNanos = System.nanoTime(); } + @Override + public Address locationRootAddress() { + return to.rootAddress(); + } + public long endTimeNanos() { return startTimeNanos + (((long) timerLengthMillis()) * 1000000); } @@ -82,10 +87,7 @@ public boolean isDue() { } @Override - public int compareTo(TimerEnvelope o) { - if (o == null) { - return 1; - } + public int compareTo(@NonNull TimerEnvelope o) { return Long.compare(endTimeNanos(), o.endTimeNanos()); } diff --git a/framework/tst/dslabs/framework/testing/runner/Network.java b/framework/tst/dslabs/framework/testing/runner/Network.java index 49bfbedf..007d2dbd 100644 --- a/framework/tst/dslabs/framework/testing/runner/Network.java +++ b/framework/tst/dslabs/framework/testing/runner/Network.java @@ -102,13 +102,13 @@ Event take() throws InterruptedException { newTimerEndTime.set(Long.MAX_VALUE); TimerEnvelope te = timers.peek(); if (te != null && te.isDue()) { - return new Event(timers.poll()); + return timers.poll(); } newMessageAvailable = false; MessageEnvelope me = messages.poll(); if (me != null) { - return new Event(me); + return me; } // Wait for new message or timer @@ -129,7 +129,7 @@ Event take() throws InterruptedException { long endTime = te.endTimeNanos(); long waitTime = endTime - System.nanoTime(); if (waitTime <= MIN_WAIT_TIME_NANOS) { - return new Event(timers.poll()); + return timers.poll(); } synchronized (this) { diff --git a/framework/tst/dslabs/framework/testing/runner/RunState.java b/framework/tst/dslabs/framework/testing/runner/RunState.java index 1da89ec6..b45b6c18 100644 --- a/framework/tst/dslabs/framework/testing/runner/RunState.java +++ b/framework/tst/dslabs/framework/testing/runner/RunState.java @@ -139,17 +139,16 @@ private void runNode(Address address, Node node, Inbox inbox) { break; } - if (item.isMessage()) { - MessageEnvelope me = item.message(); - if (settings.shouldDeliver(me)) { - node.handleMessage(me.message(), me.from(), me.to()); + switch (item) { + case MessageEnvelope me -> { + if (settings.shouldDeliver(me)) { + node.handleMessage(me.message(), me.from(), me.to()); + } } - } - - if (item.isTimer()) { - TimerEnvelope te = item.timer(); - if (settings.deliverTimers()) { - node.onTimer(te.timer(), te.to()); + case TimerEnvelope te -> { + if (settings.deliverTimers()) { + node.onTimer(te.timer(), te.to()); + } } } diff --git a/framework/tst/dslabs/framework/testing/search/SearchState.java b/framework/tst/dslabs/framework/testing/search/SearchState.java index 58af82d5..568ae7d7 100644 --- a/framework/tst/dslabs/framework/testing/search/SearchState.java +++ b/framework/tst/dslabs/framework/testing/search/SearchState.java @@ -235,7 +235,7 @@ Collection events(SearchSettings settings) { // Deliver all possible messages for (MessageEnvelope message : network) { if (hasNode(message.to().rootAddress()) && settings.shouldDeliver(message)) { - events.add(new Event(message)); + events.add(message); } } @@ -243,7 +243,7 @@ Collection events(SearchSettings settings) { for (Address address : addresses()) { if (settings.deliverTimers(address)) { for (TimerEnvelope timer : timers.get(address).deliverable()) { - events.add(new Event(timer)); + events.add(timer); } } } @@ -273,16 +273,10 @@ Collection step(SearchSettings settings) { } public SearchState stepEvent(Event event, SearchSettings settings, boolean skipChecks) { - // TODO: use enum for event type - - if (event.isMessage()) { - return stepMessage(event.message(), settings, skipChecks); - } - if (event.isTimer()) { - return stepTimer(event.timer(), settings, skipChecks); - } - - return null; + return switch (event) { + case MessageEnvelope messageEnvelope -> stepMessage(messageEnvelope, settings, skipChecks); + case TimerEnvelope timerEnvelope -> stepTimer(timerEnvelope, settings, skipChecks); + }; } public SearchState stepMessage( @@ -299,7 +293,7 @@ public SearchState stepMessage( return null; } - SearchState ns = new SearchState(this, toAddress, new Event(message)); + SearchState ns = new SearchState(this, toAddress, message); Message nm = Cloning.clone(message.message()); Node n = ns.node(toAddress); @@ -355,7 +349,7 @@ public SearchState stepTimer(TimerEnvelope timer, SearchSettings settings, boole return null; } - SearchState ns = new SearchState(this, toAddress, new Event(timer)); + SearchState ns = new SearchState(this, toAddress, timer); Timer nt = Cloning.clone(timer.timer()); Node n = ns.node(toAddress); @@ -397,8 +391,7 @@ class GraphNode { Event event = state.previousEvent; GraphNode node = new GraphNode(event); - if (event.isMessage()) { - MessageEnvelope me = event.message(); + if (event instanceof MessageEnvelope me) { if (whenSent.containsKey(me)) { GraphNode p = whenSent.get(me); p.next.add(node); diff --git a/framework/tst/dslabs/framework/testing/search/SerializableTrace.java b/framework/tst/dslabs/framework/testing/search/SerializableTrace.java index fadb7747..10bc2173 100644 --- a/framework/tst/dslabs/framework/testing/search/SerializableTrace.java +++ b/framework/tst/dslabs/framework/testing/search/SerializableTrace.java @@ -58,7 +58,7 @@ @Getter public class SerializableTrace implements Serializable { // Increment this when compatability is broken - @Serial private static final long serialVersionUID = 42L; + @Serial private static final long serialVersionUID = 43L; private static final String TRACE_DIR_NAME = "traces", TRACE_FILE_EXTENSION = ".trace"; diff --git a/framework/tst/dslabs/framework/testing/utils/CheckLogger.java b/framework/tst/dslabs/framework/testing/utils/CheckLogger.java index 97dbfa5c..24d6d133 100644 --- a/framework/tst/dslabs/framework/testing/utils/CheckLogger.java +++ b/framework/tst/dslabs/framework/testing/utils/CheckLogger.java @@ -25,6 +25,8 @@ import dslabs.framework.Node; import dslabs.framework.testing.ClientWorker; import dslabs.framework.testing.Event; +import dslabs.framework.testing.MessageEnvelope; +import dslabs.framework.testing.TimerEnvelope; import dslabs.framework.testing.Workload; import dslabs.framework.testing.search.SearchState; import java.util.Map; @@ -53,15 +55,13 @@ public final class CheckLogger { } private static String methodName(Event event, SearchState state) { - String methodName; - if (event.isMessage()) { - methodName = "handle" + event.message().message().getClass().getSimpleName(); - } else if (event.isTimer()) { - methodName = "on" + event.timer().timer().getClass().getSimpleName(); - } else { - // Don't handle other methods for now - return null; - } + String methodName = + switch (event) { + case MessageEnvelope messageEnvelope -> + "handle" + messageEnvelope.message().getClass().getSimpleName(); + case TimerEnvelope timerEnvelope -> + "on" + timerEnvelope.timer().getClass().getSimpleName(); + }; Node n = state.node(event.locationRootAddress().rootAddress()); if (n instanceof ClientWorker) { diff --git a/framework/tst/dslabs/framework/testing/visualization/EventTreeState.java b/framework/tst/dslabs/framework/testing/visualization/EventTreeState.java index f7b2c5ac..6c2e00fe 100644 --- a/framework/tst/dslabs/framework/testing/visualization/EventTreeState.java +++ b/framework/tst/dslabs/framework/testing/visualization/EventTreeState.java @@ -91,8 +91,8 @@ private EventTreeState(@NonNull SearchState state, EventTreeState parent) { assert e != null; Set temp = new HashSet<>(parent.undeliveredMessages); // Remove this message from the message list - if (e.isMessage()) { - temp.remove(e.message()); + if (e instanceof MessageEnvelope) { + temp.remove(e); } temp.addAll(state.newMessages()); undeliveredMessages = ImmutableSet.copyOf(temp); @@ -200,7 +200,7 @@ boolean sendsDeliveredMessages() { continue; } Event e = state.previousEvent(); - if (e.isMessage() && !state.parent().undeliveredMessages().contains(e.message())) { + if (e instanceof MessageEnvelope && !state.parent().undeliveredMessages().contains(e)) { return true; } } diff --git a/framework/tst/dslabs/framework/testing/visualization/EventVisibilityPane.java b/framework/tst/dslabs/framework/testing/visualization/EventVisibilityPane.java index baddcbc5..549fe2e6 100644 --- a/framework/tst/dslabs/framework/testing/visualization/EventVisibilityPane.java +++ b/framework/tst/dslabs/framework/testing/visualization/EventVisibilityPane.java @@ -27,6 +27,8 @@ import dslabs.framework.Message; import dslabs.framework.Timer; import dslabs.framework.testing.Event; +import dslabs.framework.testing.MessageEnvelope; +import dslabs.framework.testing.TimerEnvelope; import java.util.Comparator; import java.util.Map.Entry; import java.util.SortedMap; @@ -150,7 +152,10 @@ boolean isHidden(Timer t) { } boolean isHidden(Event e) { - return e.isMessage() ? isHidden(e.message().message()) : isHidden(e.timer().timer()); + return switch (e) { + case MessageEnvelope messageEnvelope -> isHidden(messageEnvelope.message()); + case TimerEnvelope timerEnvelope -> isHidden(timerEnvelope.timer()); + }; } } diff --git a/framework/tst/dslabs/framework/testing/visualization/EventsPanel.java b/framework/tst/dslabs/framework/testing/visualization/EventsPanel.java index 0e6ae834..6c0906b0 100644 --- a/framework/tst/dslabs/framework/testing/visualization/EventsPanel.java +++ b/framework/tst/dslabs/framework/testing/visualization/EventsPanel.java @@ -89,8 +89,7 @@ private ObjectJTree addEventTreeState(final EventTreeState s) { button.setToolTipText("Go to the state after this timer was delivered"); } - Event e = s.previousEvent(); - ObjectJTree tree = new ObjectJTree(e.isMessage() ? e.message() : e.timer()); + ObjectJTree tree = new ObjectJTree(s.previousEvent()); tree.collapseRow(0); box.add(tree); diff --git a/framework/tst/dslabs/framework/testing/visualization/SingleNodePanel.java b/framework/tst/dslabs/framework/testing/visualization/SingleNodePanel.java index efec6f4f..99c71dd2 100644 --- a/framework/tst/dslabs/framework/testing/visualization/SingleNodePanel.java +++ b/framework/tst/dslabs/framework/testing/visualization/SingleNodePanel.java @@ -27,7 +27,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import dslabs.framework.Address; -import dslabs.framework.testing.Event; import dslabs.framework.testing.MessageEnvelope; import dslabs.framework.testing.TimerEnvelope; import dslabs.framework.testing.search.SearchSettings; @@ -325,7 +324,7 @@ private void addMessage( JButton deliveryButton = new JButton(Utils.makeIcon(FontAwesome.DOWNLOAD)); deliveryButton.setFocusable(false); mbox.add(deliveryButton, "pad 0 0"); - deliveryButton.addActionListener(e -> parent.deliverEvent(new Event(message))); + deliveryButton.addActionListener(e -> parent.deliverEvent(message)); setDeliverability(deliveryButton, pruned, prohibited, exception, "message"); ObjectJTree tree = new ObjectJTree(message); @@ -348,7 +347,7 @@ private static Pair timerPanel( final JButton deliveryButton = new JButton(Utils.makeIcon(FontAwesome.DOWNLOAD)); deliveryButton.setFocusable(false); - deliveryButton.addActionListener(e -> parent.deliverEvent(new Event(timer))); + deliveryButton.addActionListener(e -> parent.deliverEvent(timer)); tbox.add(deliveryButton, "pad 0 0"); if (!deliverable) { diff --git a/labs/lab2-primarybackup/tst/dslabs/primarybackup/PrimaryBackupTest.java b/labs/lab2-primarybackup/tst/dslabs/primarybackup/PrimaryBackupTest.java index 33d04334..f60f11f7 100644 --- a/labs/lab2-primarybackup/tst/dslabs/primarybackup/PrimaryBackupTest.java +++ b/labs/lab2-primarybackup/tst/dslabs/primarybackup/PrimaryBackupTest.java @@ -212,16 +212,15 @@ private View getView() { fail("Interrupted while waiting for view"); } - MessageEnvelope me = e.message(); - if (me == null) { - fail("Polled envelope is null (this should never happen)"); - } - Message m = me.message(); - if (!(m instanceof ViewReply)) { - fail("Got non-ViewReply message in response to GetView"); + if (e instanceof MessageEnvelope me) { + if (me.message() instanceof ViewReply viewReply) { + return viewReply.view(); + } else { + fail("Got non-ViewReply message in response to GetView"); + } } - return ((ViewReply) m).view(); + throw new AssertionError("Polled event is not a message (this should never happen)"); } /**