diff --git a/.idea/libraries/instrumentation.jar b/.idea/libraries/instrumentation.jar
new file mode 100644
index 0000000000000000000000000000000000000000..02650a5823feaa94ca08dc868d2f6c0c2e0ecb8c
Binary files /dev/null and b/.idea/libraries/instrumentation.jar differ
diff --git a/.idea/libraries/org_javassist_javassist_3_29_2_GA.xml b/.idea/libraries/org_javassist_javassist_3_29_2_GA.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bb45507d06a495ef86dcec92a57632555e5fcb97
--- /dev/null
+++ b/.idea/libraries/org_javassist_javassist_3_29_2_GA.xml
@@ -0,0 +1,10 @@
+<component name="libraryTable">
+  <library name="org.javassist:javassist:3.29.2-GA" type="repository">
+    <properties maven-id="org.javassist:javassist:3.29.2-GA" />
+    <CLASSES>
+      <root url="jar://$MAVEN_REPOSITORY$/org/javassist/javassist/3.29.2-GA/javassist-3.29.2-GA.jar!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 9adcf7a41db750b7096f4846ea07e37851b7cb0a..690aa52958d8a3bb82f51cde7f720dcee043903d 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -9,7 +9,7 @@
   <component name="PreferredVcsStorage">
     <preferredVcsName>ApexVCS</preferredVcsName>
   </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_15" default="true" project-jdk-name="19" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/out" />
   </component>
-</project>
+</project>
\ No newline at end of file
diff --git a/.idea/runConfigurations/A_Tests.xml b/.idea/runConfigurations/A_Tests.xml
index 8b16bd9f65d0aa5a0ba0817f36d38242a5a5b7d0..116af227697288cb31401958c5c56aa4165e54a8 100644
--- a/.idea/runConfigurations/A_Tests.xml
+++ b/.idea/runConfigurations/A_Tests.xml
@@ -4,6 +4,7 @@
     <option name="MAIN_CLASS_NAME" value="" />
     <option name="METHOD_NAME" value="" />
     <option name="TEST_OBJECT" value="tags" />
+    <option name="VM_PARAMETERS" value="-noverify -ea -javaagent:.idea/libraries/instrumentation.jar" />
     <option name="PARAMETERS" value="" />
     <option name="TEST_SEARCH_SCOPE">
       <value defaultName="wholeProject" />
@@ -13,4 +14,4 @@
       <option name="Make" enabled="true" />
     </method>
   </configuration>
-</component>
\ No newline at end of file
+</component>
diff --git a/.idea/runConfigurations/B_Tests.xml b/.idea/runConfigurations/B_Tests.xml
index 417d3543526097de0a40fa4e22c7d421b4b05ea7..519be8d9342e4e1846419edf74be404872cf3afe 100644
--- a/.idea/runConfigurations/B_Tests.xml
+++ b/.idea/runConfigurations/B_Tests.xml
@@ -4,6 +4,7 @@
     <option name="MAIN_CLASS_NAME" value="" />
     <option name="METHOD_NAME" value="" />
     <option name="TEST_OBJECT" value="tags" />
+    <option name="VM_PARAMETERS" value="-noverify -ea -javaagent:.idea/libraries/instrumentation.jar" />
     <option name="PARAMETERS" value="" />
     <option name="TEST_SEARCH_SCOPE">
       <value defaultName="wholeProject" />
@@ -13,4 +14,4 @@
       <option name="Make" enabled="true" />
     </method>
   </configuration>
-</component>
\ No newline at end of file
+</component>
diff --git a/.idea/runConfigurations/C_Tests.xml b/.idea/runConfigurations/C_Tests.xml
index d1cfa143e2b01de9466460ff0481f4a813f22ab6..3564619629e35217ed0481c4bf4b3b69d7e79314 100644
--- a/.idea/runConfigurations/C_Tests.xml
+++ b/.idea/runConfigurations/C_Tests.xml
@@ -4,6 +4,7 @@
     <option name="MAIN_CLASS_NAME" value="" />
     <option name="METHOD_NAME" value="" />
     <option name="TEST_OBJECT" value="tags" />
+    <option name="VM_PARAMETERS" value="-noverify -ea -javaagent:.idea/libraries/instrumentation.jar" />
     <option name="PARAMETERS" value="" />
     <option name="TEST_SEARCH_SCOPE">
       <value defaultName="wholeProject" />
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
deleted file mode 100644
index 6209a3f20c61db22a340bd72534c186e1a937e00..0000000000000000000000000000000000000000
--- a/.idea/workspace.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="ChangeListManager">
-    <list default="true" id="59dc1d65-5f60-42d1-aafd-1f41614da1fe" name="Changes" comment="">
-      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/edu/caltech/cs2/datastructures/ArrayDeque.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/edu/caltech/cs2/datastructures/ArrayDeque.java" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/src/edu/caltech/cs2/datastructures/LinkedDeque.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/edu/caltech/cs2/datastructures/LinkedDeque.java" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/tests/edu/caltech/cs2/helpers/Inspection.java" beforeDir="false" afterPath="$PROJECT_DIR$/tests/edu/caltech/cs2/helpers/Inspection.java" afterDir="false" />
-    </list>
-    <option name="SHOW_DIALOG" value="false" />
-    <option name="HIGHLIGHT_CONFLICTS" value="true" />
-    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
-    <option name="LAST_RESOLUTION" value="IGNORE" />
-  </component>
-  <component name="Git.Settings">
-    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
-  </component>
-  <component name="MarkdownSettingsMigration">
-    <option name="stateVersion" value="1" />
-  </component>
-  <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
-  <component name="RunManager" selected="JUnit.C Tests">
-    <list>
-      <item itemvalue="JUnit.C Tests" />
-      <item itemvalue="JUnit.B Tests" />
-      <item itemvalue="JUnit.A Tests" />
-    </list>
-  </component>
-  <component name="TaskManager">
-    <servers />
-  </component>
-</project>
\ No newline at end of file
diff --git a/project03-guitar.iml b/project03-guitar.iml
index 3ba9bf360538af064c1595c7671c8f0b0836cd69..9f76aeaa06d3487fd37771962a8c33797149328c 100644
--- a/project03-guitar.iml
+++ b/project03-guitar.iml
@@ -15,5 +15,6 @@
     <orderEntry type="library" name="org.junit.jupiter:junit-jupiter:5.6.0-M1" level="project" />
     <orderEntry type="library" name="org.hamcrest:hamcrest:2.2" level="project" />
     <orderEntry type="library" name="org.hamcrest:hamcrest-core:2.2" level="project" />
+    <orderEntry type="library" name="org.javassist:javassist:3.29.2-GA" level="project" />
   </component>
 </module>
\ No newline at end of file
diff --git a/src/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueue.java b/src/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueue.java
index 2ff895914bcee17cea22ab9b658e38d9bfa1dd7b..aaba2b5ce2a9a9274e0ef4138122ace28d9a7a27 100644
--- a/src/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueue.java
+++ b/src/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueue.java
@@ -6,6 +6,10 @@ import java.util.Iterator;
 
 public class CircularArrayFixedSizeQueue<E> implements IFixedSizeQueue<E> {
 
+    public CircularArrayFixedSizeQueue(int capacity) {
+
+    }
+
     @Override
     public boolean isFull() {
         return false;
@@ -36,6 +40,16 @@ public class CircularArrayFixedSizeQueue<E> implements IFixedSizeQueue<E> {
         return 0;
     }
 
+    @Override
+    public void add(E e) {
+
+    }
+
+    @Override
+    public void clear() {
+
+    }
+
     @Override
     public Iterator<E> iterator() {
         return null;
diff --git a/src/edu/caltech/cs2/interfaces/IQueue.java b/src/edu/caltech/cs2/interfaces/IQueue.java
index ec9b94c7f234052c0fc2e618b9f62b1eb709a205..a4628d22a40ffbf75da9ee4b6727022823259d1e 100644
--- a/src/edu/caltech/cs2/interfaces/IQueue.java
+++ b/src/edu/caltech/cs2/interfaces/IQueue.java
@@ -4,7 +4,7 @@ package edu.caltech.cs2.interfaces;
  * This interface represents a queue - a data structure that can add elements at one end and remove them from the other.
  * @param <E> Element type
  */
-public interface IQueue<E> extends Iterable<E> {
+public interface IQueue<E> extends Iterable<E>, ICollection<E> {
   /**
    * Adds an element to the back of the queue.
    * @param e Element to add
diff --git a/tests/edu/caltech/cs2/datastructures/ArrayDequeTests.java b/tests/edu/caltech/cs2/datastructures/ArrayDequeTests.java
index fecbcc7b346cff3f3c478ebe3d4ab651a75b69aa..79e554cc6f7f4379cc04fe4960d84dcd99310149 100644
--- a/tests/edu/caltech/cs2/datastructures/ArrayDequeTests.java
+++ b/tests/edu/caltech/cs2/datastructures/ArrayDequeTests.java
@@ -1,13 +1,13 @@
 package edu.caltech.cs2.datastructures;
 
-import edu.caltech.cs2.helpers.Inspection;
-import edu.caltech.cs2.helpers.Reflection;
-import edu.caltech.cs2.helpers.RuntimeInstrumentation;
+import edu.caltech.cs2.helpers.*;
 import edu.caltech.cs2.interfaces.*;
 import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
+import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.util.*;
@@ -21,302 +21,326 @@ import static org.junit.jupiter.api.Assertions.*;
 
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
 @Tag("C")
-public class ArrayDequeTests implements IDequeTests, IStackTests, IQueueTests {
-  private static String ARRAY_DEQUE_SOURCE ="src/edu/caltech/cs2/datastructures/ArrayDeque.java";
+@ExtendWith(TestExtension.class)
+public class ArrayDequeTests {
+  private static String ARRAY_DEQUE_SOURCE = "src/edu/caltech/cs2/datastructures/ArrayDeque.java";
 
   private Constructor arrayDequeConstructor = Reflection.getConstructor(ArrayDeque.class);
 
-  public ICollection<Object> newCollection() {
-    return Reflection.newInstance(arrayDequeConstructor);
-  }
-
-  public IDeque<Object> newDeque() {
-    return Reflection.newInstance(arrayDequeConstructor);
-  }
-
-  public IStack<Object> newStack() {
-    return Reflection.newInstance(arrayDequeConstructor);
-  }
-
-  public IQueue<Object> newQueue() {
-      return Reflection.newInstance(arrayDequeConstructor);
-  }
-
-  public IQueue<Object> newQueue(int size) {
-    return newQueue();
-  }
-
   // ARRAYDEQUE-SPECIFIC TESTS ----------------------------------------
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("Does not use or import disallowed classes")
-  @Test
-  public void testForInvalidClasses() {
-    List<String> regexps = List.of("java\\.util\\.(?!Iterator)", "java\\.lang\\.reflect", "java\\.io");
-    Inspection.assertNoImportsOf(ARRAY_DEQUE_SOURCE, regexps);
-    Inspection.assertNoUsageOf(ARRAY_DEQUE_SOURCE, regexps);
-  }
+  @DisplayName("Style")
+  @Nested
+  class StyleTests implements IStyleTests {
+    @Order(classSpecificTestLevel)
+    @DisplayName("There is an integer default capacity static field and an integer default grow factor static field")
+    @TestDescription("This test checks that you use constants (static, final, private) fields for the default capacity and the growth factor when the array resizes.")
+    @Test
+    public void testConstantFields() {
+      Reflection.assertFieldsEqualTo(ArrayDeque.class, "static", 2);
+      Stream<Field> fields = Reflection.getFields(ArrayDeque.class);
+      fields.filter(Reflection.hasModifier("static")).forEach((field) -> {
+        Reflection.checkFieldModifiers(field, List.of("private", "static", "final"));
+        assertEquals(int.class, field.getType(), "static fields must be of type int");
+      });
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There is an integer default capacity static field and an integer default grow factor static field")
-  @Test
-  public void testConstantFields() {
-    Reflection.assertFieldsEqualTo(ArrayDeque.class, "static", 2);
-    Stream<Field> fields = Reflection.getFields(ArrayDeque.class);
-    fields.filter(Reflection.hasModifier("static")).forEach((field) -> {
-      Reflection.checkFieldModifiers(field, List.of("private", "static", "final"));
-      assertEquals(int.class, field.getType(), "static fields must be of type int");
-    });
-  }
+    public int getMaxFields() {
+      return 5;
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("The overall number of fields is small")
-  @Test
-  public void testSmallNumberOfFields() {
-    Reflection.assertFieldsLessThan(ArrayDeque.class, "private", 5);
-  }
+    public List<String> getPublicInterface() {
+      return List.of(
+              "addFront",
+              "addBack",
+              "removeFront",
+              "removeBack",
+              "enqueue",
+              "dequeue",
+              "push",
+              "pop",
+              "peek",
+              "peekFront",
+              "peekBack",
+              "iterator",
+              "size",
+              "toString"
+      );
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no public fields")
-  @Test
-  public void testNoPublicFields() {
-    Reflection.assertNoPublicFields(ArrayDeque.class);
-  }
+    @Override
+    public String getSource() {
+      return ARRAY_DEQUE_SOURCE;
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no protected fields")
-  @Test
-  public void testNoProtectedFields() {
-    Reflection.assertNoProtectedFields(ArrayDeque.class);
+    @Override
+    public Class<?> getClazz() {
+      return ArrayDeque.class;
+    }
   }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("All fields in ArrayDeque have modifiers")
-  @Test
-  public void testFieldModifiers() {
-    Reflection.assertFieldModifiers(ArrayDeque.class);
-  }
+  // TIME COMPLEXITY TESTS ------------------------------------------------
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("The public interface is correct")
-  @Test
-  public void testPublicInterface() {
-    Reflection.assertPublicInterface(ArrayDeque.class, List.of(
-            "addFront",
-            "addBack",
-            "removeFront",
-            "removeBack",
-            "enqueue",
-            "dequeue",
-            "push",
-            "pop",
-            "peek",
-            "peekFront",
-            "peekBack",
-            "iterator",
-            "size",
-            "toString"
-    ));
-  }
+  @Nested
+  @DisplayName("Runtime Complexity")
+  class RuntimeTests {
+    @Order(complexityTestLevel)
+    @DisplayName("addFront() and removeFront() take linear time")
+    @Timeout(value = 20, unit = SECONDS)
+    @Test()
+    public void testFrontDequeOperationComplexity() {
+      Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
+        IDeque<Integer> q = new ArrayDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.addFront(i);
+        }
+        return q;
+      };
+      Consumer<IDeque<Integer>> addFront = (IDeque<Integer> q) -> q.addFront(0);
+      Consumer<IDeque<Integer>> removeFront = (IDeque<Integer> q) -> q.removeFront();
+
+      RuntimeInstrumentation.assertAtMost("addFront", RuntimeInstrumentation.ComplexityType.LINEAR, provide, addFront, 8);
+      RuntimeInstrumentation.assertAtMost("removeFront", RuntimeInstrumentation.ComplexityType.LINEAR, provide, removeFront, 8);
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("Uses this(...) notation in all but one constructor")
-  @Test
-  public void testForThisConstructors() {
-    Inspection.assertConstructorHygiene(ARRAY_DEQUE_SOURCE);
-  }
+    @Order(complexityTestLevel)
+    @DisplayName("addBack() and removeBack() take constant time")
+    @Timeout(value = 20, unit = SECONDS)
+    @Test
+    public void testBackDequeOperationComplexity() {
+      Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
+        IDeque<Integer> q = new ArrayDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.addBack(i);
+        }
+        return q;
+      };
+      Consumer<IDeque<Integer>> addBack = (IDeque<Integer> q) -> q.addBack(0);
+      Consumer<IDeque<Integer>> removeBack = (IDeque<Integer> q) -> q.removeBack();
+
+      RuntimeInstrumentation.assertAtMost("addBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, addBack, 8);
+      RuntimeInstrumentation.assertAtMost("removeBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, removeBack, 8);
+    }
 
-  // ARRAYDEQUE TESTS ---------------------------------------------------
+    @Order(complexityTestLevel)
+    @DisplayName("enqueue() and dequeue() take linear time")
+    @Timeout(value = 20, unit = SECONDS)
+    @Test
+    public void testQueueOperationComplexity() {
+      Function<Integer, IQueue<Integer>> provide = (Integer numElements) -> {
+        IQueue<Integer> q = new ArrayDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.enqueue(i);
+        }
+        return q;
+      };
+      Consumer<IQueue<Integer>> enqueue = (IQueue<Integer> q) -> q.enqueue(0);
+      Consumer<IQueue<Integer>> dequeue = (IQueue<Integer> q) -> q.dequeue();
+
+      RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.LINEAR, provide, enqueue, 8);
+      RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.LINEAR, provide, dequeue, 8);
+    }
 
-  @Order(implSpecificTestLevel)
-  @DisplayName("The default capacity of the array is 10")
-  @Test
-  public void testArrayDequeDefaultInitialCapacity() throws IllegalAccessException {
-    ArrayDeque<Integer> impl = new ArrayDeque<>();
+    @Order(complexityTestLevel)
+    @DisplayName("push() and pop() take constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testStackOperationComplexity() {
+      Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
+        IStack<Integer> q = new ArrayDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.push(i);
+        }
+        return q;
+      };
+      Consumer<IStack<Integer>> push = (IStack<Integer> q) -> q.push(0);
+      Consumer<IStack<Integer>> pop = (IStack<Integer> q) -> q.pop();
+
+      RuntimeInstrumentation.assertAtMost("push", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, push, 8);
+      RuntimeInstrumentation.assertAtMost("pop", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, pop, 8);
+    }
 
-    // Reflect and get the backing array
-    // It's actually an Object[] since that's how it (should!) be initialized internally
-    // Casting it doesn't change the type of the field.
-    // It's fine since there should only be one array.
-    Field arr = Reflection.getFieldByType(ArrayDeque.class, Object[].class);
-    arr.setAccessible(true);
-    Object[] backingArray = (Object[]) arr.get(impl);
+    @Order(complexityTestLevel)
+    @DisplayName("peek() takes constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testPeekComplexity() {
+      Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
+        IStack<Integer> q = new ArrayDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.push(i);
+        }
+        return q;
+      };
+      Consumer<IStack<Integer>> peek = (IStack<Integer> q) -> q.peek();
+
+      RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
+    }
 
-    assertEquals(10, backingArray.length, "Default initial capacity is not 10");
-  }
+    @Order(complexityTestLevel)
+    @DisplayName("peekFront() takes constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test()
+    public void testPeekFrontComplexity() {
+      Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
+        IDeque<Integer> q = new ArrayDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.addFront(i);
+        }
+        return q;
+      };
+      Consumer<IDeque<Integer>> peekFront = (IDeque<Integer> q) -> q.peekFront();
+
+      RuntimeInstrumentation.assertAtMost("peekFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekFront, 8);
+    }
 
-  @Order(implSpecificTestLevel)
-  @DisplayName("enqueue should always succeed")
-  @Test
-  public void testThatArrayDequeEnqueueAlwaysSucceeds() {
-    ArrayDeque<Integer> impl = new ArrayDeque<>();
-    for (int i = 0; i < 100; i ++) {
-      assertTrue(impl.enqueue(i), "enqueue() should always succeed for ArrayDeque");
+    @Order(complexityTestLevel)
+    @DisplayName("peekBack() takes constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testPeekBackComplexity() {
+      Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
+        IDeque<Integer> q = new ArrayDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.addBack(i);
+        }
+        return q;
+      };
+      Consumer<IDeque<Integer>> peekBack = (IDeque<Integer> q) -> q.peekBack();
+
+      RuntimeInstrumentation.assertAtMost("peekBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekBack, 8);
     }
   }
 
-  @Order(implSpecificTestLevel)
-  @DisplayName("push should always succeed")
-  @Test
-  public void testThatArrayDequePushAlwaysSucceeds() {
-    ArrayDeque<Integer> impl = new ArrayDeque<>();
-    for (int i = 0; i < 100; i ++) {
-      assertTrue(impl.push(i), "push() should always succeed for ArrayDeque");
+  @Nested
+  @DisplayName("IStack Functionality")
+  class StackTests implements IStackTests {
+    @Override
+    public IStack<Object> newStack() {
+      return Reflection.newInstance(arrayDequeConstructor);
     }
   }
 
-  // TOSTRING TESTS ---------------------------------------------------
+  @Nested
+  @DisplayName("IQueue Functionality")
+  class QueueTests implements IQueueTests {
 
-  @Order(toStringTestLevel)
-  @DisplayName("toString is correctly overridden")
-  @Test
-  public void testToStringOverride() {
-    Reflection.assertMethodCorrectlyOverridden(ArrayDeque.class, "toString");
-  }
+    public IQueue<Object> newQueue() {
+      return Reflection.newInstance(arrayDequeConstructor);
+    }
 
-  @Order(toStringTestLevel)
-  @DisplayName("toString() matches java.util.ArrayDeque")
-  @ParameterizedTest(name = "Test toString() on [{arguments}]")
-  @ValueSource(strings = {
-          "0, 1, 2, 3", "5, 4, 3, 2, 1", "8, 3, 5, 7, 4, 3, 12, 12, 1"
-  })
-  public void testToString(String inputs) {
-    java.util.ArrayDeque<String> reference = new java.util.ArrayDeque<String>();
-    edu.caltech.cs2.datastructures.ArrayDeque<String> me = new edu.caltech.cs2.datastructures.ArrayDeque<>();
-    for (String value : inputs.trim().split(", ")) {
-      assertEquals(reference.toString(), me.toString(), "toString outputs should be the same");
-      reference.addLast(value);
-      me.addBack(value);
+    public IQueue<Object> newQueue(int size) {
+      return newQueue();
     }
   }
 
-  // TIME COMPLEXITY TESTS ------------------------------------------------
-
-  @Order(complexityTestLevel)
-  @DisplayName("addFront() and removeFront() take linear time")
-  @Timeout(value = 20, unit = SECONDS)
-  @Test()
-  public void testFrontDequeOperationComplexity() {
-    Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
-      IDeque<Integer> q = new ArrayDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.addFront(i);
-      }
-      return q;
-    };
-    Consumer<IDeque<Integer>> addFront = (IDeque<Integer> q) -> q.addFront(0);
-    Consumer<IDeque<Integer>> removeFront = (IDeque<Integer> q) -> q.removeFront();
-
-    RuntimeInstrumentation.assertAtMost("addFront", RuntimeInstrumentation.ComplexityType.LINEAR, provide, addFront, 8);
-    RuntimeInstrumentation.assertAtMost("removeFront", RuntimeInstrumentation.ComplexityType.LINEAR, provide, removeFront, 8);
+  @Nested
+  @DisplayName("IDeque Functionality")
+  class DequeTests implements IDequeTests {
+    @Override
+    public IDeque<Object> newDeque() {
+      return Reflection.newInstance(arrayDequeConstructor);
+    }
   }
 
-  @Order(complexityTestLevel)
-  @DisplayName("addBack() and removeBack() take constant time")
-  @Timeout(value = 20, unit = SECONDS)
-  @Test
-  public void testBackDequeOperationComplexity() {
-    Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
-      IDeque<Integer> q = new ArrayDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.addBack(i);
-      }
-      return q;
-    };
-    Consumer<IDeque<Integer>> addBack = (IDeque<Integer> q) -> q.addBack(0);
-    Consumer<IDeque<Integer>> removeBack = (IDeque<Integer> q) -> q.removeBack();
-
-    RuntimeInstrumentation.assertAtMost("addBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, addBack, 8);
-    RuntimeInstrumentation.assertAtMost("removeBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, removeBack, 8);
+  @Nested
+  @DisplayName("ICollection Functionality")
+  class CollectionTests implements ICollectionTests {
+    @Override
+    public ICollection<Object> newCollection() {
+      return Reflection.newInstance(arrayDequeConstructor);
+    }
   }
 
-  @Order(complexityTestLevel)
-  @DisplayName("enqueue() and dequeue() take linear time")
-  @Timeout(value = 20, unit = SECONDS)
-  @Test
-  public void testQueueOperationComplexity() {
-    Function<Integer, IQueue<Integer>> provide = (Integer numElements) -> {
-      IQueue<Integer> q = new ArrayDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.enqueue(i);
-      }
-      return q;
-    };
-    Consumer<IQueue<Integer>> enqueue = (IQueue<Integer> q) -> q.enqueue(0);
-    Consumer<IQueue<Integer>> dequeue = (IQueue<Integer> q) -> q.dequeue();
-
-    RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.LINEAR, provide, enqueue, 8);
-    RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.LINEAR, provide, dequeue, 8);
-  }
+  @DisplayName("Implementation Tests")
+  @Nested
+  class ImplementationTests {
+    @Order(implSpecificTestLevel)
+    @DisplayName("The default capacity of the array in the deque is 10")
+    @DependsOn({"fields", "constructors"})
+    @Test
+    public void testArrayDequeDefaultInitialCapacity() throws IllegalAccessException {
+      ArrayDeque<Integer> impl = new ArrayDeque<>();
+
+      // Reflect and get the backing array
+      // It's actually an Object[] since that's how it (should!) be initialized internally
+      // Casting it doesn't change the type of the field.
+      // It's fine since there should only be one array.
+      Field arr = Reflection.getFieldByType(ArrayDeque.class, Object[].class);
+      arr.setAccessible(true);
+      Object[] backingArray = (Object[]) arr.get(impl);
+
+      assertEquals(10, backingArray.length, "Default initial capacity is not 10");
+    }
 
-  @Order(complexityTestLevel)
-  @DisplayName("push() and pop() take constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testStackOperationComplexity() {
-    Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
-      IStack<Integer> q = new ArrayDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.push(i);
+    @Order(implSpecificTestLevel)
+    @DisplayName("enqueue should always succeed")
+    @TestDescription("This test is checking every enqueue in your ArrayDeque returns true.\nThe only time an enqueue might return false is if it doesn't resize (like in the B Tests).")
+    @DependsOn({"fields", "constructors", "enqueue"})
+    @Test
+    public void testThatArrayDequeEnqueueAlwaysSucceeds() {
+      ArrayDeque<Integer> impl = new ArrayDeque<>();
+      for (int i = 0; i < 100; i++) {
+        assertTrue(impl.enqueue(i), "enqueue() should always succeed for ArrayDeque");
       }
-      return q;
-    };
-    Consumer<IStack<Integer>> push = (IStack<Integer> q) -> q.push(0);
-    Consumer<IStack<Integer>> pop = (IStack<Integer> q) -> q.pop();
-
-    RuntimeInstrumentation.assertAtMost("push", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, push, 8);
-    RuntimeInstrumentation.assertAtMost("pop", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, pop, 8);
-  }
+    }
 
-  @Order(complexityTestLevel)
-  @DisplayName("peek() takes constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testPeekComplexity() {
-    Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
-      IStack<Integer> q = new ArrayDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.push(i);
+    @Order(implSpecificTestLevel)
+    @DisplayName("Check for excessive array allocation in addFront and removeFront")
+    @TestDescription("This test is checking that you are not allocating extra arrays in add/remove that are not necessary.")
+    @DependsOn({"fields", "constructors", "addFront", "removeFront"})
+    @Test
+    public void testForExcessiveArrayAllocationAddFront() {
+      NewObjectArray.NUM_CALLS = 0;
+      ArrayDeque<Integer> impl = new ArrayDeque<>();
+      for (int i = 0; i < 100; i++) {
+        int before = NewObjectArray.NUM_CALLS;
+        impl.addFront(i);
+        int after = NewObjectArray.NUM_CALLS;
+        assertTrue(before + 1 >= after, "Each addFront() should create at most one new array");
       }
-      return q;
-    };
-    Consumer<IStack<Integer>> peek = (IStack<Integer> q) -> q.peek();
-
-    RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
-  }
-
-  @Order(complexityTestLevel)
-  @DisplayName("peekFront() takes constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test()
-  public void testPeekFrontComplexity() {
-    Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
-      IDeque<Integer> q = new ArrayDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.addFront(i);
+      assertTrue(NewObjectArray.NUM_CALLS < 2 * ((int) (Math.log(100) / Math.log(2))), "addFront() should not use an excessive number of new arrays");
+      for (int i = 0; i < 100; i++) {
+        int before = NewObjectArray.NUM_CALLS;
+        impl.removeFront();
+        int after = NewObjectArray.NUM_CALLS;
+        assertTrue(before == after, "removeFront() should not allocate any new arrays");
       }
-      return q;
-    };
-    Consumer<IDeque<Integer>> peekFront = (IDeque<Integer> q) -> q.peekFront();
-
-    RuntimeInstrumentation.assertAtMost("peekFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekFront, 8);
-  }
+    }
 
-  @Order(complexityTestLevel)
-  @DisplayName("peekBack() takes constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testPeekBackComplexity() {
-    Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
-      IDeque<Integer> q = new ArrayDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.addBack(i);
+    @Order(implSpecificTestLevel)
+    @DisplayName("Check for excessive array allocation in addBack and removeBack")
+    @TestDescription("This test is checking that you are not allocating extra arrays in add/remove that are not necessary.")
+    @DependsOn({"fields", "constructors", "addBack", "removeBack"})
+    @Test
+    public void testForExcessiveArrayAllocationAddBack() {
+      NewObjectArray.NUM_CALLS = 0;
+      ArrayDeque<Integer> impl = new ArrayDeque<>();
+      for (int i = 0; i < 100; i++) {
+        int before = NewObjectArray.NUM_CALLS;
+        impl.addBack(i);
+        int after = NewObjectArray.NUM_CALLS;
+        assertTrue(before + 1 >= after, "Each addBack() should create at most one new array");
       }
-      return q;
-    };
-    Consumer<IDeque<Integer>> peekBack = (IDeque<Integer> q) -> q.peekBack();
+      assertTrue(NewObjectArray.NUM_CALLS < 2 * ((int) (Math.log(100) / Math.log(2))), "addBack() should not use an excessive number of new arrays");
+      for (int i = 0; i < 100; i++) {
+        int before = NewObjectArray.NUM_CALLS;
+        impl.removeBack();
+        int after = NewObjectArray.NUM_CALLS;
+        assertTrue(before == after, "removeBack() should not allocate any new arrays");
+      }
+    }
 
-    RuntimeInstrumentation.assertAtMost("peekBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekBack, 8);
+    @Order(implSpecificTestLevel)
+    @DisplayName("push should always succeed")
+    @TestDescription("This test is checking every push in your ArrayDeque returns true.\nThe only time a push might return false is if it doesn't resize (like in the B Tests).")
+    @DependsOn({"fields", "constructors", "push"})
+    @Test
+    public void testThatArrayDequePushAlwaysSucceeds() {
+      ArrayDeque<Integer> impl = new ArrayDeque<>();
+      for (int i = 0; i < 100; i++) {
+        assertTrue(impl.push(i), "push() should always succeed for ArrayDeque");
+      }
+    }
   }
-
-}
\ No newline at end of file
+}
diff --git a/tests/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueueTests.java b/tests/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueueTests.java
index c9e2b8236b1a80c320b1a767ca0c6599d4d15b4d..8eb3d9904ae3ce9cfae041aba17dbfa125d01065 100644
--- a/tests/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueueTests.java
+++ b/tests/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueueTests.java
@@ -1,20 +1,19 @@
 package edu.caltech.cs2.datastructures;
 
-import edu.caltech.cs2.helpers.Inspection;
-import edu.caltech.cs2.helpers.Reflection;
-import edu.caltech.cs2.helpers.RuntimeInstrumentation;
-import edu.caltech.cs2.interfaces.IFixedSizeQueueTests;
-import edu.caltech.cs2.interfaces.IFixedSizeQueue;
-import edu.caltech.cs2.interfaces.IQueue;
+import edu.caltech.cs2.helpers.*;
+import edu.caltech.cs2.interfaces.*;
 import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.CsvSource;
 import org.junit.jupiter.params.provider.ValueSource;
 
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
 import java.util.*;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.stream.Stream;
 
 import static edu.caltech.cs2.project03.Project03TestOrdering.*;
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -22,174 +21,153 @@ import static org.junit.jupiter.api.Assertions.*;
 
 @Tag("B")
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-public class CircularArrayFixedSizeQueueTests implements IFixedSizeQueueTests {
+@ExtendWith(TestExtension.class)
+public class CircularArrayFixedSizeQueueTests {
   private static String FIXED_QUEUE_SOURCE = "src/edu/caltech/cs2/datastructures/CircularArrayFixedSizeQueue.java";
 
   private Constructor circFixedSizeQueueConstructor = Reflection.getConstructor(CircularArrayFixedSizeQueue.class,
       int.class);
-  private int DEFAULT_CAPACITY = 10;
-
-  public IQueue<Object> newQueue() {
-    return Reflection.newInstance(circFixedSizeQueueConstructor, DEFAULT_CAPACITY);
-  }
-
-  public IQueue<Object> newQueue(int capacity) {
-    return Reflection.newInstance(circFixedSizeQueueConstructor, capacity);
-  }
 
   public IFixedSizeQueue<Object> newFixedSizeQueue(int capacity) {
     return Reflection.newInstance(circFixedSizeQueueConstructor, capacity);
   }
 
-  // FIXED QUEUE-SPECIFIC TESTS ----------------------------------------
-
-  @Order(classSpecificTestLevel)
-  @DisplayName("Does not use or import disallowed classes")
-  @Test
-  public void testForInvalidClasses() {
-    List<String> regexps = List.of("java\\.util\\.(?!Iterator)", "java\\.lang\\.reflect", "java\\.io");
-    Inspection.assertNoImportsOf(FIXED_QUEUE_SOURCE, regexps);
-    Inspection.assertNoUsageOf(FIXED_QUEUE_SOURCE, regexps);
-  }
+  @DisplayName("Style")
+  @Nested
+  class StyleTests implements IStyleTests {
+    public int getMaxFields() {
+      return 4;
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no static fields")
-  @Test
-  public void testConstantFields() {
-    Reflection.assertFieldsEqualTo(CircularArrayFixedSizeQueue.class, "static", 0);
-  }
+    public List<String> getPublicInterface() {
+      return List.of("enqueue", "dequeue", "peek", "iterator", "size", "isFull", "capacity", "toString", "add", "clear");
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("The overall number of fields is small")
-  @Test
-  public void testSmallNumberOfFields() {
-    Reflection.assertFieldsLessThan(CircularArrayFixedSizeQueue.class, "private", 4);
-  }
+    @Override
+    public String getSource() {
+      return FIXED_QUEUE_SOURCE;
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no public fields")
-  @Test
-  public void testNoPublicFields() {
-    Reflection.assertNoPublicFields(CircularArrayFixedSizeQueue.class);
+    @Override
+    public Class<?> getClazz() {
+      return CircularArrayFixedSizeQueue.class;
+    }
   }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no protected fields")
-  @Test
-  public void testNoProtectedFields() {
-    Reflection.assertNoProtectedFields(CircularArrayFixedSizeQueue.class);
-  }
+  @Nested
+  @DisplayName("Runtime Complexity")
+  class RuntimeTests {
+    @Order(complexityTestLevel)
+    @DisplayName("enqueue() and dequeue() take constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test()
+    public void testQueueOperationComplexity() {
+      Function<Integer, IFixedSizeQueue<Integer>> provide = (Integer numElements) -> {
+        Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
+        IFixedSizeQueue<Integer> q = Reflection.newInstance(c, numElements * 2);
+        for (int i = 0; i < numElements; i++) {
+          q.enqueue(i);
+        }
+        return q;
+      };
+      Consumer<IFixedSizeQueue<Integer>> enqueue = (IFixedSizeQueue<Integer> q) -> q.enqueue(0);
+      Consumer<IFixedSizeQueue<Integer>> dequeue = (IFixedSizeQueue<Integer> q) -> q.dequeue();
+
+      RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, enqueue, 8);
+      RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, dequeue, 8);
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("All fields in CircularArrayFixedSizeQueue have modifiers")
-  @Test
-  public void testFieldModifiers() {
-    Reflection.assertFieldModifiers(CircularArrayFixedSizeQueue.class);
+    @Order(complexityTestLevel)
+    @DisplayName("peek() takes constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test()
+    public void testPeekComplexity() {
+      Function<Integer, IFixedSizeQueue<Integer>> provide = (Integer numElements) -> {
+        Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
+        IFixedSizeQueue<Integer> q = Reflection.newInstance(c, numElements * 2);
+        for (int i = 0; i < numElements; i++) {
+          q.enqueue(i);
+        }
+        return q;
+      };
+      Consumer<IFixedSizeQueue<Integer>> peek = (IFixedSizeQueue<Integer> q) -> q.peek();
+
+      RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
+    }
   }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("The public interface is correct")
-  @Test
-  public void testPublicInterface() {
-    Reflection.assertPublicInterface(CircularArrayFixedSizeQueue.class,
-        List.of("enqueue", "dequeue", "peek", "iterator", "size", "isFull", "capacity", "toString"));
-  }
+  @Nested
+  @DisplayName("IFixedSizeQueue Functionality")
+  class QueueTests implements IFixedSizeQueueTests {
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("Uses this(...) notation in all but one constructor")
-  @Test
-  public void testForThisConstructors() {
-    Inspection.assertConstructorHygiene(FIXED_QUEUE_SOURCE);
-  }
+    public IFixedSizeQueue<Object> newQueue() {
+      return Reflection.newInstance(circFixedSizeQueueConstructor);
+    }
 
-  // TOSTRING TESTS ---------------------------------------------------
+    public IFixedSizeQueue<Object> newQueue(int size) {
+      return Reflection.newInstance(circFixedSizeQueueConstructor, size);
+    }
 
-  @Order(toStringTestLevel)
-  @DisplayName("toString is correctly overridden")
-  @Test
-  public void testToStringOverride() {
-    Reflection.assertMethodCorrectlyOverridden(ArrayDeque.class, "toString");
+    @Override
+    public IFixedSizeQueue<Object> newFixedSizeQueue(int capacity) {
+      return CircularArrayFixedSizeQueueTests.this.newFixedSizeQueue(capacity);
+    }
   }
 
-  @Order(toStringTestLevel)
-  @DisplayName("toString() matches java.util.ArrayDeque")
-  @ParameterizedTest(name = "Test toString() on [{arguments}]")
-  @ValueSource(strings = { "0, 1, 2, 3", "5, 4, 3, 2, 1", "8, 3, 5, 7, 4, 3, 12, 12, 1" })
-  public void testToString(String inputs) {
-    java.util.ArrayDeque<String> reference = new java.util.ArrayDeque<String>();
-    Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
-    IFixedSizeQueue<String> me = Reflection.newInstance(c, inputs.length());
-    for (String value : inputs.trim().split(", ")) {
-      assertEquals(reference.toString(), me.toString(), "toString outputs should be the same");
-      reference.addLast(value);
-      me.enqueue(value);
+  @Nested
+  @DisplayName("ICollection Functionality")
+  class CollectionTests implements ICollectionTests {
+    @Override
+    public ICollection<Object> newCollection() {
+      return Reflection.newInstance(circFixedSizeQueueConstructor, 100000);
     }
   }
 
-  // TIME COMPLEXITY TESTS ------------------------------------------------
-
-  @Order(complexityTestLevel)
-  @DisplayName("enqueue() and dequeue() take constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test()
-  public void testQueueOperationComplexity() {
-    Function<Integer, IFixedSizeQueue<Integer>> provide = (Integer numElements) -> {
-      Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
-      IFixedSizeQueue<Integer> q = Reflection.newInstance(c, numElements * 2);
-      for (int i = 0; i < numElements; i++) {
-        q.enqueue(i);
-      }
-      return q;
-    };
-    Consumer<IFixedSizeQueue<Integer>> enqueue = (IFixedSizeQueue<Integer> q) -> q.enqueue(0);
-    Consumer<IFixedSizeQueue<Integer>> dequeue = (IFixedSizeQueue<Integer> q) -> q.dequeue();
-
-    RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, enqueue, 8);
-    RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, dequeue, 8);
-  }
+  @DisplayName("Implementation Tests")
+  @Nested
+  class ImplementationTests {
+    @Order(fixedSizeQueueLevel)
+    @DisplayName("Test iterator matches reference for wraparound values")
+    @TestHint("This test is likely failing because you don't have correct wrap-around behavior.\n     Remember that everything should advance; so, it's possible that your start wraps around.")
+    @ParameterizedTest(name = "Test iterator and wraparound behavior with {1} random values with seed = {0} and fixed array size = {2}")
+    @CsvSource({"69, 200, 20", "21, 300, 200"})
+    public void testWrapAround(int seed, int numVals, int queueSize) {
+      Random r = new Random(seed);
+      IFixedSizeQueue<Object> me = newFixedSizeQueue(queueSize);
+      Queue<Object> reference = new java.util.ArrayDeque<>();
+      assertEquals(queueSize, me.capacity(), "capacity does not match expected value");
+      for (int i = 0; i < queueSize; i++) {
+        int num = r.nextInt();
+        assertFalse(me.isFull(), "queue should not be full");
+        assertTrue(me.enqueue(num), "enqueue should be successful");
+        reference.add(num);
 
-  @Order(complexityTestLevel)
-  @DisplayName("peek() takes constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test()
-  public void testPeekComplexity() {
-    Function<Integer, IFixedSizeQueue<Integer>> provide = (Integer numElements) -> {
-      Constructor c = Reflection.getConstructor(CircularArrayFixedSizeQueue.class, int.class);
-      IFixedSizeQueue<Integer> q = Reflection.newInstance(c, numElements * 2);
-      for (int i = 0; i < numElements; i++) {
-        q.enqueue(i);
       }
-      return q;
-    };
-    Consumer<IFixedSizeQueue<Integer>> peek = (IFixedSizeQueue<Integer> q) -> q.peek();
-
-    RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
-  }
-
-  @Order(fixedSizeQueueLevel)
-  @DisplayName("Test iterator matches reference for wraparound values")
-  @ParameterizedTest(name = "Test iterator and wraparound behavior with {1} random values with seed = {0} and fixed array size = {2}")
-  @CsvSource({ "69, 200, 20", "21, 300, 200" })
-  public void testWrapAround(int seed, int numVals, int queueSize) {
-    Random r = new Random(seed);
-    IFixedSizeQueue<Object> me = newFixedSizeQueue(queueSize);
-    Queue<Object> reference = new java.util.ArrayDeque<>();
-    assertEquals(queueSize, me.capacity(), "capacity does not match expected value");
-    for (int i = 0; i < queueSize; i++) {
-      int num = r.nextInt();
-      assertFalse(me.isFull(), "queue should not be full");
-      assertTrue(me.enqueue(num), "enqueue should be successful");
-      reference.add(num);
-
+      for (int i = 0; i < numVals; i++) {
+        me.enqueue(me.dequeue());
+        reference.add(reference.remove());
+        assertEquals(reference.peek(), me.peek(), "return values of peek()s are not equal");
+        assertEquals(reference.size(), me.size(), "size()s are not equal");
+        assertEquals(queueSize, me.capacity(), "capacity of a fixed size queue should not change");
+        assertIterableEquals(reference, me, "Reference and implemented queues are not equal");
+      }
     }
-    for (int i = 0; i < numVals; i++) {
-      me.enqueue(me.dequeue());
-      reference.add(reference.remove());
-      assertEquals(reference.peek(), me.peek(), "return values of peek()s are not equal");
-      assertEquals(reference.size(), me.size(), "size()s are not equal");
-      assertEquals(queueSize, me.capacity(), "capacity of a fixed size queue should not change");
-      assertIterableEquals(reference, me, "Reference and implemented queues are not equal");
+
+    @Order(toStringTestLevel)
+    @DisplayName("toString() matches java.util.ArrayDeque")
+    @DependsOn({"fields", "constructors", "toString", "enqueue"})
+    @ParameterizedTest(name = "Test toString() on [{arguments}]")
+    @ValueSource(strings = {
+            "0, 1, 2, 3", "5, 4, 3, 2, 1", "8, 3, 5, 7, 4, 3, 12, 12, 1"
+    })
+    public void testToString(String inputs) {
+      java.util.ArrayDeque<String> reference = new java.util.ArrayDeque<String>();
+      edu.caltech.cs2.interfaces.IFixedSizeQueue<Object> me = new CircularArrayFixedSizeQueue<>(10);
+      for (String value : inputs.trim().split(", ")) {
+        assertEquals(reference.toString(), me.toString(), "toString outputs should be the same");
+        reference.addLast(value);
+        me.enqueue(value);
+      }
     }
   }
-
 }
diff --git a/tests/edu/caltech/cs2/datastructures/LinkedDequeTests.java b/tests/edu/caltech/cs2/datastructures/LinkedDequeTests.java
index 83fd0af46977ce79f3d9f662cc96638da37cb576..6c336b5e877971357227380c43b1ee3a07179798 100644
--- a/tests/edu/caltech/cs2/datastructures/LinkedDequeTests.java
+++ b/tests/edu/caltech/cs2/datastructures/LinkedDequeTests.java
@@ -1,357 +1,380 @@
 package edu.caltech.cs2.datastructures;
 
-import edu.caltech.cs2.helpers.Inspection;
-import edu.caltech.cs2.helpers.NodeChecker;
-import edu.caltech.cs2.helpers.Reflection;
-import edu.caltech.cs2.helpers.RuntimeInstrumentation;
+import edu.caltech.cs2.helpers.*;
 import edu.caltech.cs2.interfaces.*;
 import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.CsvSource;
-import org.junit.jupiter.params.provider.ValueSource;
 
 import java.lang.reflect.Constructor;
 import java.util.*;
-import java.util.ArrayDeque;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
 import static edu.caltech.cs2.project03.Project03TestOrdering.*;
 import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @Tag("C")
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-public class LinkedDequeTests implements IDequeTests, IStackTests, IQueueTests {
+@ExtendWith(TestExtension.class)
+public class LinkedDequeTests {
   private static String LINKED_DEQUE_SOURCE = "src/edu/caltech/cs2/datastructures/LinkedDeque.java";
 
   private Constructor linkedDequeConstructor = Reflection.getConstructor(LinkedDeque.class);
 
-  public ICollection<Object> newCollection() {
-    return Reflection.newInstance(linkedDequeConstructor);
-  }
+  // LINKEDDEQUE-SPECIFIC TESTS ----------------------------------------
+  @DisplayName("Style")
+  @Nested
+  class StyleTests implements IStyleTests {
+    @Order(classSpecificTestLevel)
+    @DisplayName("There are no static fields")
+    @TestHint("Remember that static fields belong to the class instead of the instance.  For this class, you shouldn't need any.")
+    @Test
+    public void testConstantFields() {
+      Reflection.assertFieldsEqualTo(LinkedDeque.class, "static", 0);
+    }
 
-  public IDeque<Object> newDeque() {
-    return Reflection.newInstance(linkedDequeConstructor);
-  }
+    @Override
+    public String getSource() {
+      return LINKED_DEQUE_SOURCE;
+    }
 
-  public IStack<Object> newStack() {
-    return Reflection.newInstance(linkedDequeConstructor);
-  }
+    @Override
+    public Class<?> getClazz() {
+      return LinkedDeque.class;
+    }
 
-  public IQueue<Object> newQueue() {
-    return Reflection.newInstance(linkedDequeConstructor);
-  }
+    @Override
+    public List<String> getPublicInterface() {
+      return List.of("addFront", "addBack", "removeFront", "removeBack",
+              "enqueue", "dequeue", "push", "pop", "peek", "peekFront", "peekBack", "iterator", "size", "toString");
+    }
+
+    @Override
+    public int getMaxFields() {
+      return 4;
+    }
 
-  public IQueue<Object> newQueue(int size) {
-    return newQueue();
   }
 
-  // LINKEDDEQUE-SPECIFIC TESTS ----------------------------------------
+  @Nested
+  @DisplayName("Runtime Complexity")
+  class RuntimeTests {
+    @Order(complexityTestLevel)
+    @DisplayName("addFront() and removeFront() take constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testFrontDequeOperationComplexity() {
+      Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
+        IDeque<Integer> q = new LinkedDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.addFront(i);
+        }
+        return q;
+      };
+      Consumer<IDeque<Integer>> addFront = (IDeque<Integer> q) -> q.addFront(0);
+      Consumer<IDeque<Integer>> removeFront = (IDeque<Integer> q) -> q.removeFront();
+
+      RuntimeInstrumentation.assertAtMost("addFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, addFront,
+              8);
+      RuntimeInstrumentation.assertAtMost("removeFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide,
+              removeFront, 8);
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("Does not use or import disallowed classes")
-  @Test
-  public void testForInvalidClasses() {
-    List<String> regexps = List.of("java\\.util\\.(?!Iterator)", "java\\.lang\\.reflect", "java\\.io");
-    Inspection.assertNoImportsOf(LINKED_DEQUE_SOURCE, regexps);
-    Inspection.assertNoUsageOf(LINKED_DEQUE_SOURCE, regexps);
-  }
+    @Order(complexityTestLevel)
+    @DisplayName("addBack() and removeBack() take constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testBackDequeOperationComplexity() {
+      Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
+        IDeque<Integer> q = new LinkedDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.addBack(i);
+        }
+        return q;
+      };
+      Consumer<IDeque<Integer>> addBack = (IDeque<Integer> q) -> q.addBack(0);
+      Consumer<IDeque<Integer>> removeBack = (IDeque<Integer> q) -> q.removeBack();
+
+      RuntimeInstrumentation.assertAtMost("addBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, addBack, 8);
+      RuntimeInstrumentation.assertAtMost("removeBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide,
+              removeBack, 8);
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no static fields")
-  @Test
-  public void testConstantFields() {
-    Reflection.assertFieldsEqualTo(LinkedDeque.class, "static", 0);
-  }
+    @Order(complexityTestLevel)
+    @DisplayName("enqueue() and dequeue() take constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testQueueOperationComplexity() {
+      Function<Integer, IQueue<Integer>> provide = (Integer numElements) -> {
+        IQueue<Integer> q = new LinkedDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.enqueue(i);
+        }
+        return q;
+      };
+      Consumer<IQueue<Integer>> enqueue = (IQueue<Integer> q) -> q.enqueue(0);
+      Consumer<IQueue<Integer>> dequeue = (IQueue<Integer> q) -> q.dequeue();
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("The overall number of fields is small")
-  @Test
-  public void testSmallNumberOfFields() {
-    Reflection.assertFieldsLessThan(LinkedDeque.class, "private", 4);
-  }
+      RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, enqueue, 8);
+      RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, dequeue, 8);
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no public fields")
-  @Test
-  public void testNoPublicFields() {
-    Reflection.assertNoPublicFields(LinkedDeque.class);
-  }
+    @Order(complexityTestLevel)
+    @DisplayName("push() and pop() take constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testStackOperationComplexity() {
+      Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
+        IStack<Integer> q = new LinkedDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.push(i);
+        }
+        return q;
+      };
+      Consumer<IStack<Integer>> push = (IStack<Integer> q) -> q.push(0);
+      Consumer<IStack<Integer>> pop = (IStack<Integer> q) -> q.pop();
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no protected fields")
-  @Test
-  public void testNoProtectedFields() {
-    Reflection.assertNoProtectedFields(LinkedDeque.class);
-  }
+      RuntimeInstrumentation.assertAtMost("push", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, push, 8);
+      RuntimeInstrumentation.assertAtMost("pop", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, pop, 8);
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("All fields in LinkedDeque have modifiers")
-  @Test
-  public void testFieldModifiers() {
-    Reflection.assertFieldModifiers(LinkedDeque.class);
-  }
+    @Order(complexityTestLevel)
+    @DisplayName("peek() takes constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testPeekComplexity() {
+      Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
+        IStack<Integer> q = new LinkedDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.push(i);
+        }
+        return q;
+      };
+      Consumer<IStack<Integer>> peek = (IStack<Integer> q) -> q.peek();
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("The public interface is correct")
-  @Test
-  public void testPublicInterface() {
-    Reflection.assertPublicInterface(LinkedDeque.class, List.of("addFront", "addBack", "removeFront", "removeBack",
-        "enqueue", "dequeue", "push", "pop", "peek", "peekFront", "peekBack", "iterator", "size", "toString"));
-  }
+      RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("Uses this(...) notation in all but one constructor")
-  @Test
-  public void testForThisConstructors() {
-    Inspection.assertConstructorHygiene(LINKED_DEQUE_SOURCE);
-  }
+    @Order(complexityTestLevel)
+    @DisplayName("peekFront() takes constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testPeekFrontComplexity() {
+      Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
+        IDeque<Integer> q = new LinkedDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.addFront(i);
+        }
+        return q;
+      };
+      Consumer<IDeque<Integer>> peekFront = (IDeque<Integer> q) -> q.peekFront();
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("Check that LinkedDeque uses a node class")
-  @Test
-  public void testLinkedNode() {
-    Class[] classes = LinkedDeque.class.getDeclaredClasses();
-    for (Class clazz : classes) {
-      if (Iterator.class.isAssignableFrom(clazz)) {
-        continue;
-      }
-      NodeChecker.isNode(clazz, true);
+      RuntimeInstrumentation.assertAtMost("peekFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekFront,
+              8);
     }
-  }
 
-  // TOSTRING TESTS ---------------------------------------------------
+    @Order(complexityTestLevel)
+    @DisplayName("peekBack() takes constant time")
+    @Timeout(value = 10, unit = SECONDS)
+    @Test
+    public void testPeekBackComplexity() {
+      Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
+        IDeque<Integer> q = new LinkedDeque<>();
+        for (int i = 0; i < numElements; i++) {
+          q.addBack(i);
+        }
+        return q;
+      };
+      Consumer<IDeque<Integer>> peekBack = (IDeque<Integer> q) -> q.peekBack();
 
-  @Order(toStringTestLevel)
-  @DisplayName("toString is correctly overridden")
-  @Test
-  public void testToStringOverride() {
-    Reflection.assertMethodCorrectlyOverridden(LinkedDeque.class, "toString");
+      RuntimeInstrumentation.assertAtMost("peekBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekBack,
+              8);
+    }
   }
 
-  @Order(toStringTestLevel)
-  @DisplayName("toString() matches java.util.ArrayDeque")
-  @ParameterizedTest(name = "Test toString() on [{arguments}]")
-  @ValueSource(strings = { "0, 1, 2, 3", "5, 4, 3, 2, 1", "8, 3, 5, 7, 4, 3, 12, 12, 1" })
-  public void testToString(String inputs) {
-    java.util.ArrayDeque<String> reference = new java.util.ArrayDeque<String>();
-    LinkedDeque<String> me = new LinkedDeque<>();
-    for (String value : inputs.trim().split(", ")) {
-      assertEquals(reference.toString(), me.toString(), "toString outputs should be the same");
-      reference.addLast(value);
-      me.addBack(value);
+  @Nested
+  @DisplayName("IStack Functionality")
+  class StackTests implements IStackTests {
+    @Override
+    public IStack<Object> newStack() {
+      return Reflection.newInstance(linkedDequeConstructor);
     }
   }
 
-  // TIME COMPLEXITY TESTS ------------------------------------------------
-
-  @Order(complexityTestLevel)
-  @DisplayName("addFront() and removeFront() take constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testFrontDequeOperationComplexity() {
-    Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
-      IDeque<Integer> q = new LinkedDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.addFront(i);
-      }
-      return q;
-    };
-    Consumer<IDeque<Integer>> addFront = (IDeque<Integer> q) -> q.addFront(0);
-    Consumer<IDeque<Integer>> removeFront = (IDeque<Integer> q) -> q.removeFront();
-
-    RuntimeInstrumentation.assertAtMost("addFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, addFront,
-        8);
-    RuntimeInstrumentation.assertAtMost("removeFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide,
-        removeFront, 8);
+  @Nested
+  @DisplayName("IQueue Functionality")
+  class QueueTests implements IQueueTests {
+    public IQueue<Object> newQueue() {
+      return Reflection.newInstance(linkedDequeConstructor);
+    }
+    public IQueue<Object> newQueue(int size) {
+      return newQueue();
+    }
   }
 
-  @Order(complexityTestLevel)
-  @DisplayName("addBack() and removeBack() take constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testBackDequeOperationComplexity() {
-    Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
-      IDeque<Integer> q = new LinkedDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.addBack(i);
-      }
-      return q;
-    };
-    Consumer<IDeque<Integer>> addBack = (IDeque<Integer> q) -> q.addBack(0);
-    Consumer<IDeque<Integer>> removeBack = (IDeque<Integer> q) -> q.removeBack();
-
-    RuntimeInstrumentation.assertAtMost("addBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, addBack, 8);
-    RuntimeInstrumentation.assertAtMost("removeBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide,
-        removeBack, 8);
+  @Nested
+  @DisplayName("IDeque Functionality")
+  class DequeTests implements IDequeTests {
+    @Override
+    public IDeque<Object> newDeque() {
+      return Reflection.newInstance(linkedDequeConstructor);
+    }
   }
 
-  @Order(complexityTestLevel)
-  @DisplayName("enqueue() and dequeue() take constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testQueueOperationComplexity() {
-    Function<Integer, IQueue<Integer>> provide = (Integer numElements) -> {
-      IQueue<Integer> q = new LinkedDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.enqueue(i);
-      }
-      return q;
-    };
-    Consumer<IQueue<Integer>> enqueue = (IQueue<Integer> q) -> q.enqueue(0);
-    Consumer<IQueue<Integer>> dequeue = (IQueue<Integer> q) -> q.dequeue();
-
-    RuntimeInstrumentation.assertAtMost("enqueue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, enqueue, 8);
-    RuntimeInstrumentation.assertAtMost("dequeue", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, dequeue, 8);
+  @Nested
+  @DisplayName("ICollection Functionality")
+  class CollectionTests implements ICollectionTests {
+    @Override
+    public ICollection<Object> newCollection() {
+      return Reflection.newInstance(linkedDequeConstructor);
+    }
   }
 
-  @Order(complexityTestLevel)
-  @DisplayName("push() and pop() take constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testStackOperationComplexity() {
-    Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
-      IStack<Integer> q = new LinkedDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.push(i);
+  @DisplayName("Implementation Tests")
+  @Nested
+  class ImplementationTests {
+    @Order(implSpecificTestLevel)
+    @DisplayName("Check for excessive node allocation in addFront and removeFront")
+    @TestDescription("This test is checking that you are not allocating extra nodes in add/remove that are not necessary.")
+    @DependsOn({"fields", "constructors", "addFront", "removeFront"})
+    @Test
+    public void testForExcessiveNodeAllocationAddFront() {
+      NewNode.NUM_CALLS = 0;
+      edu.caltech.cs2.datastructures.LinkedDeque<Integer> impl = new edu.caltech.cs2.datastructures.LinkedDeque<>();
+      for (int i = 0; i < 100; i++) {
+        int before = NewNode.NUM_CALLS;
+        impl.addFront(i);
+        int after = NewNode.NUM_CALLS;
+        assertTrue(before + 1 >= after, "Each addFront() should create at most one new node");
       }
-      return q;
-    };
-    Consumer<IStack<Integer>> push = (IStack<Integer> q) -> q.push(0);
-    Consumer<IStack<Integer>> pop = (IStack<Integer> q) -> q.pop();
-
-    RuntimeInstrumentation.assertAtMost("push", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, push, 8);
-    RuntimeInstrumentation.assertAtMost("pop", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, pop, 8);
-  }
-
-  @Order(complexityTestLevel)
-  @DisplayName("peek() takes constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testPeekComplexity() {
-    Function<Integer, IStack<Integer>> provide = (Integer numElements) -> {
-      IStack<Integer> q = new LinkedDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.push(i);
+      for (int i = 0; i < 100; i++) {
+        int before = NewNode.NUM_CALLS;
+        impl.removeFront();
+        int after = NewNode.NUM_CALLS;
+        assertTrue(before == after, "removeFront() should not allocate any new nodes");
       }
-      return q;
-    };
-    Consumer<IStack<Integer>> peek = (IStack<Integer> q) -> q.peek();
-
-    RuntimeInstrumentation.assertAtMost("peek", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peek, 8);
-  }
+    }
 
-  @Order(complexityTestLevel)
-  @DisplayName("peekFront() takes constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testPeekFrontComplexity() {
-    Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
-      IDeque<Integer> q = new LinkedDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.addFront(i);
+    @Order(implSpecificTestLevel)
+    @DisplayName("Check for excessive node allocation in addBack and removeBack")
+    @TestDescription("This test is checking that you are not allocating extra nodes in add/remove that are not necessary.")
+    @DependsOn({"fields", "constructors", "addBack", "removeBack"})
+    @Test
+    public void testForExcessiveNodeAllocationAddBack() {
+      NewNode.NUM_CALLS = 0;
+      edu.caltech.cs2.datastructures.LinkedDeque<Integer> impl = new edu.caltech.cs2.datastructures.LinkedDeque<>();
+      for (int i = 0; i < 100; i++) {
+        int before = NewNode.NUM_CALLS;
+        impl.addBack(i);
+        int after = NewNode.NUM_CALLS;
+        assertTrue(before + 1 >= after, "Each addBack() should create at most one new node");
       }
-      return q;
-    };
-    Consumer<IDeque<Integer>> peekFront = (IDeque<Integer> q) -> q.peekFront();
-
-    RuntimeInstrumentation.assertAtMost("peekFront", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekFront,
-        8);
-  }
-
-  @Order(complexityTestLevel)
-  @DisplayName("peekBack() takes constant time")
-  @Timeout(value = 10, unit = SECONDS)
-  @Test
-  public void testPeekBackComplexity() {
-    Function<Integer, IDeque<Integer>> provide = (Integer numElements) -> {
-      IDeque<Integer> q = new LinkedDeque<>();
-      for (int i = 0; i < numElements; i++) {
-        q.addBack(i);
+      for (int i = 0; i < 100; i++) {
+        int before = NewNode.NUM_CALLS;
+        impl.removeBack();
+        int after = NewNode.NUM_CALLS;
+        assertTrue(before == after, "removeBack() should not allocate any new nodes");
       }
-      return q;
-    };
-    Consumer<IDeque<Integer>> peekBack = (IDeque<Integer> q) -> q.peekBack();
-
-    RuntimeInstrumentation.assertAtMost("peekBack", RuntimeInstrumentation.ComplexityType.CONSTANT, provide, peekBack,
-        8);
-  }
+    }
 
-  // "LINKED-NESS" TESTS ------------------------------------------------
-
-  @Order(dequeTestLevel)
-  @DisplayName("Cycle detection for addFront(...), addBack(...), removeFront(...), and removeBack(...)")
-  @ParameterizedTest(name = "Test cycles - {1} random numbers with seed = {0}")
-  @CsvSource({ "69, 2000", "20, 3000" })
-  public void checkForCycles(int seed, int size) {
-    Random r = new Random(seed);
-    Deque<Object> reference = new ArrayDeque<>();
-    IDeque<Object> impl = new LinkedDeque<>();
-    // Test that first peek is null
-    assertNull(impl.peekFront(), "empty peek should return null");
-    // Randomly add / remove elements to the front / back
-    for (int i = 0; i < size; i++) {
-      int num = r.nextInt();
-      if (num % 2 == 0) {
-        reference.addLast(num);
-        impl.addBack(num);
-      } else {
-        reference.addFirst(num);
-        impl.addFront(num);
-      }
-      if (reference.size() > 1 && impl.size() > 1) {
-        if (num % 5 == 0) {
-          reference.removeFirst();
-          impl.removeFront();
-        } else if (num % 7 == 0) {
-          reference.removeLast();
-          impl.removeBack();
+    @Order(classSpecificTestLevel)
+    @DisplayName("Check that LinkedDeque uses a node class")
+    @TestDescription("This test is checking that you are are using a doubly-linked-list rather than some other implementation.")
+    @Test
+    public void testLinkedNode() {
+      Class[] classes = LinkedDeque.class.getDeclaredClasses();
+      boolean found = false;
+      for (Class clazz : classes) {
+        if (Iterator.class.isAssignableFrom(clazz)) {
+          continue;
         }
+        NodeChecker.isNode(clazz, true);
+        found = true;
+      }
+      if (!found) {
+        fail("There is no node class defined in LinkedDeque.");
       }
-      // After each operation, check whether cycles have formed
-      NodeChecker.cycleDetection(impl, true);
-      // Sanity checks, though these aren't super necessary
-      assertEquals(reference.size(), impl.size(), "size()s are not equal");
-      assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
     }
-  }
 
-  @Order(dequeTestLevel)
-  @DisplayName("Check reverses for addFront(...), addBack(...), removeFront(...), and removeBack(...)")
-  @ParameterizedTest(name = "Test reverse - {1} random numbers with seed = {0}")
-  @CsvSource({ "31, 2000", "64, 3000" })
-  public void checkReverses(int seed, int size) {
-    Random r = new Random(seed);
-    Deque<Object> reference = new ArrayDeque<>();
-    IDeque<Object> impl = new LinkedDeque<>();
-    // Test that first peek is null
-    assertNull(impl.peekFront(), "empty peek should return null");
-    // Randomly add / remove elements to the front / back
-    for (int i = 0; i < size; i++) {
-      int num = r.nextInt();
-      if (num % 2 == 0) {
-        reference.addLast(num);
-        impl.addBack(num);
-      } else {
-        reference.addFirst(num);
-        impl.addFront(num);
+    // "LINKED-NESS" TESTS ------------------------------------------------
+
+    @Order(dequeTestLevel)
+    @DisplayName("Cycle detection for addFront(...), addBack(...), removeFront(...), and removeBack(...)")
+    @TestDescription("This test follows all the links in your linked list and checks if any of them result in a cycle.")
+    @ParameterizedTest(name = "Test cycles - {1} random numbers with seed = {0}")
+    @CsvSource({"69, 2000", "20, 3000"})
+    public void checkForCycles(int seed, int size) {
+      Random r = new Random(seed);
+      Deque<Object> reference = new java.util.ArrayDeque<>();
+      IDeque<Object> impl = new LinkedDeque<>();
+      // Test that first peek is null
+      assertNull(impl.peekFront(), "empty peek should return null");
+      // Randomly add / remove elements to the front / back
+      for (int i = 0; i < size; i++) {
+        int num = r.nextInt();
+        if (num % 2 == 0) {
+          reference.addLast(num);
+          impl.addBack(num);
+        } else {
+          reference.addFirst(num);
+          impl.addFront(num);
+        }
+        if (reference.size() > 1 && impl.size() > 1) {
+          if (num % 5 == 0) {
+            reference.removeFirst();
+            impl.removeFront();
+          } else if (num % 7 == 0) {
+            reference.removeLast();
+            impl.removeBack();
+          }
+        }
+        // After each operation, check whether cycles have formed
+        NodeChecker.cycleDetection(impl, true);
+        // Sanity checks, though these aren't super necessary
+        assertEquals(reference.size(), impl.size(), "size()s are not equal");
+        assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
       }
-      if (reference.size() > 1 && impl.size() > 1) {
-        if (num % 5 == 0) {
-          reference.removeFirst();
-          impl.removeFront();
-        } else if (num % 7 == 0) {
-          reference.removeLast();
-          impl.removeBack();
+    }
+
+    @Order(dequeTestLevel)
+    @DisplayName("Check reverses for addFront(...), addBack(...), removeFront(...), and removeBack(...)")
+    @TestDescription("This test follows all the links forwards and backwards to check that your links are consistent.")
+    @ParameterizedTest(name = "Test reverse - {1} random numbers with seed = {0}")
+    @CsvSource({"31, 2000", "64, 3000"})
+    public void checkReverses(int seed, int size) {
+      Random r = new Random(seed);
+      Deque<Object> reference = new java.util.ArrayDeque<>();
+      IDeque<Object> impl = new LinkedDeque<>();
+      // Test that first peek is null
+      assertNull(impl.peekFront(), "empty peek should return null");
+      // Randomly add / remove elements to the front / back
+      for (int i = 0; i < size; i++) {
+        int num = r.nextInt();
+        if (num % 2 == 0) {
+          reference.addLast(num);
+          impl.addBack(num);
+        } else {
+          reference.addFirst(num);
+          impl.addFront(num);
         }
+        if (reference.size() > 1 && impl.size() > 1) {
+          if (num % 5 == 0) {
+            reference.removeFirst();
+            impl.removeFront();
+          } else if (num % 7 == 0) {
+            reference.removeLast();
+            impl.removeBack();
+          }
+        }
+        // Check that forwards and backwards iteration are sane
+        NodeChecker.checkReverse(impl);
+        assertEquals(reference.size(), impl.size(), "size()s are not equal");
+        assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
       }
-      // Check that forwards and backwards iteration are sane
-      NodeChecker.checkReverse(impl);
-      assertEquals(reference.size(), impl.size(), "size()s are not equal");
-      assertEquals(reference.toString(), impl.toString(), "toStrings()s are not equal");
     }
   }
-
 }
\ No newline at end of file
diff --git a/tests/edu/caltech/cs2/helpers/DependsOn.java b/tests/edu/caltech/cs2/helpers/DependsOn.java
new file mode 100644
index 0000000000000000000000000000000000000000..f0c609d595c7b6177b0898cd880caf8bb20f31d0
--- /dev/null
+++ b/tests/edu/caltech/cs2/helpers/DependsOn.java
@@ -0,0 +1,12 @@
+package edu.caltech.cs2.helpers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DependsOn {
+    String[] value();
+}
diff --git a/tests/edu/caltech/cs2/helpers/NewNode.java b/tests/edu/caltech/cs2/helpers/NewNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..b96f5c28ec45462d39100e2ab4cff37944763cb1
--- /dev/null
+++ b/tests/edu/caltech/cs2/helpers/NewNode.java
@@ -0,0 +1,5 @@
+package edu.caltech.cs2.helpers;
+
+public class NewNode {
+    public static int NUM_CALLS = 0;
+}
diff --git a/tests/edu/caltech/cs2/helpers/NewObjectArray.java b/tests/edu/caltech/cs2/helpers/NewObjectArray.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c5df162990e00250f66fb30db0cd42bf0e28e81
--- /dev/null
+++ b/tests/edu/caltech/cs2/helpers/NewObjectArray.java
@@ -0,0 +1,5 @@
+package edu.caltech.cs2.helpers;
+
+public class NewObjectArray {
+    public static int NUM_CALLS = 0;
+}
diff --git a/tests/edu/caltech/cs2/helpers/RuntimeCounter.java b/tests/edu/caltech/cs2/helpers/RuntimeCounter.java
new file mode 100644
index 0000000000000000000000000000000000000000..960c1697bf234326d29712262d8ad5457dcadbb6
--- /dev/null
+++ b/tests/edu/caltech/cs2/helpers/RuntimeCounter.java
@@ -0,0 +1,9 @@
+package edu.caltech.cs2.helpers;
+
+public class RuntimeCounter {
+    public static int NUM_CALLS = 0;
+
+    public static void inc() {
+        NUM_CALLS++;
+    }
+}
diff --git a/tests/edu/caltech/cs2/helpers/RuntimeInstrumentation.java b/tests/edu/caltech/cs2/helpers/RuntimeInstrumentation.java
index d22a40a8c58dc9d9710e64ec551ca0bc6f10c5be..325051294a7625de3bce79d64376d7cb977bed91 100644
--- a/tests/edu/caltech/cs2/helpers/RuntimeInstrumentation.java
+++ b/tests/edu/caltech/cs2/helpers/RuntimeInstrumentation.java
@@ -9,8 +9,8 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 
 public class RuntimeInstrumentation {
-    private static final int SKIP = 5;
-    private static final int ITERATIONS = 100;
+    private static final int SKIP = 3;
+    private static final int ITERATIONS = 1;
 
     public enum ComplexityType {
         CONSTANT(0, "constant"),
@@ -37,10 +37,11 @@ public class RuntimeInstrumentation {
     }
 
     public static <DS> long timeFunction(DS ds, Consumer<DS> function) {
-        long startTime = System.nanoTime();
+        RuntimeCounter.NUM_CALLS = 0;
+        //long startTime = System.nanoTime();
         function.accept(ds);
-        long endTime = System.nanoTime();
-        return endTime - startTime;
+        //long endTime = System.nanoTime();
+        return RuntimeCounter.NUM_CALLS;//endTime - startTime;
     }
 
     public static <DS> ComplexityType getEmpiricalComplexity(Function<Integer, DS> provideDSOfSize, Consumer<DS> functionToTest, int numberOfDoubles) {
@@ -50,9 +51,6 @@ public class RuntimeInstrumentation {
             long totalTime = 0;
             for (int i = 0; i < ITERATIONS; i++) {
                 DS ds = provideDSOfSize.apply(currentSize);
-                // Bring ds into cache! Make sure we're only clocking
-                // the function, and not JVM operations on the heap / cache
-                timeFunction(ds, functionToTest);
                 totalTime += timeFunction(ds, functionToTest);
             }
             times.add(Math.round((double)totalTime / ITERATIONS));
@@ -62,6 +60,10 @@ public class RuntimeInstrumentation {
             times.remove(0);
         }
 
+        if (times.stream().allMatch(((x) -> x == 0))) {
+           fail("Infrastructure is broken or method is unimplemented! See course staff!");
+        }
+
         if (isApproximately(ComplexityType.CONSTANT, times)) {
             return ComplexityType.CONSTANT;
         }
@@ -152,13 +154,13 @@ public class RuntimeInstrumentation {
         // Tune depending on strictness - 0.95 accounts for variations
         // *Should* actually be like 0.99, but sometimes weird heap operations
         // happen and make certain runs take longer
-        return rSq >= 0.92;
+        return rSq >= 0.98;
     }
 
     public static <DS> void assertAtMost(String whatIsBeingTested, ComplexityType expected, Function<Integer, DS> provideDSOfSize, Consumer<DS> functionToTest, int numberOfDoubles) {
         ComplexityType calculated = getEmpiricalComplexity(provideDSOfSize, functionToTest, numberOfDoubles);
         if (calculated.isSlowerThan(expected)) {
-            fail(whatIsBeingTested + " is expected to be " + expected + " time or better. The actual calculated time is " + calculated + ".\nThis test is non-deterministic which means it might not always be correct.  If you run it multiple times and it usually passes, that's probably fine.");
+            fail(whatIsBeingTested + " is expected to be " + expected + " time or better. The actual calculated time is " + calculated + ".");//"\nThis test is non-deterministic which means it might not always be correct.  If you run it multiple times and it usually passes, that's probably fine.");
         }
     }
 }
diff --git a/tests/edu/caltech/cs2/helpers/TestDescription.java b/tests/edu/caltech/cs2/helpers/TestDescription.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac322e2bc23f118095e22e8a2c20357900be8459
--- /dev/null
+++ b/tests/edu/caltech/cs2/helpers/TestDescription.java
@@ -0,0 +1,12 @@
+package edu.caltech.cs2.helpers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TestDescription {
+    String value();
+}
diff --git a/tests/edu/caltech/cs2/helpers/TestExtension.java b/tests/edu/caltech/cs2/helpers/TestExtension.java
new file mode 100644
index 0000000000000000000000000000000000000000..e0524b4b66a30accf2a4b434cc8753afd69a164e
--- /dev/null
+++ b/tests/edu/caltech/cs2/helpers/TestExtension.java
@@ -0,0 +1,42 @@
+package edu.caltech.cs2.helpers;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;
+import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
+
+import java.util.Arrays;
+
+public class TestExtension implements TestExecutionExceptionHandler {
+    public static final String TEXT_RED = "\u001B[31m";
+    public static final String TEXT_BLACK = "\u001B[30m";
+    public static final String TEXT_GREEN = "\u001B[32m";
+    public static final String TEXT_BLUE = "\u001B[34m";
+    public static final String TEXT_RESET = "\u001B[0m";
+    public static final String TEXT_PURPLE = "\u001B[35m";
+    public static final String TEXT_CYAN = "\u001B[36m";
+    public static final String TEXT_YELLOW = "\u001B[33m";
+    public static final String TEXT_WHITE = "\u001B[37m";
+
+    @Override
+    public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
+            throws Throwable {
+        DependsOn d = context.getTestMethod().get().getAnnotation(DependsOn.class);
+        System.out.print(TEXT_YELLOW);
+        System.out.println(context.getDisplayName());
+        System.out.println("=".repeat(context.getDisplayName().length()));
+        TestDescription t2 = context.getTestMethod().get().getAnnotation(TestDescription.class);
+        if (t2 != null) {
+            System.out.println(t2.value());
+        }
+        System.out.print(TEXT_PURPLE);
+        if (d != null) {
+            System.out.println("Hint: This test depends on the following being implemented correctly:\n    - " + String.join("\n    - ", d.value()));
+        }
+        TestHint t = context.getTestMethod().get().getAnnotation(TestHint.class);
+        if (t != null) {
+            System.out.println("Hint: " + t.value());
+        }
+        System.out.print(TEXT_RESET);
+        throw throwable;
+    }
+}
\ No newline at end of file
diff --git a/tests/edu/caltech/cs2/helpers/TestHint.java b/tests/edu/caltech/cs2/helpers/TestHint.java
new file mode 100644
index 0000000000000000000000000000000000000000..d924e778395cf4a23b09cf9c70b3676aa621043b
--- /dev/null
+++ b/tests/edu/caltech/cs2/helpers/TestHint.java
@@ -0,0 +1,12 @@
+package edu.caltech.cs2.helpers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TestHint {
+    String value();
+}
diff --git a/tests/edu/caltech/cs2/interfaces/ICollectionTests.java b/tests/edu/caltech/cs2/interfaces/ICollectionTests.java
index dd97298717f56d8b07903450a6fda73915f0439a..65b1937f643955d60a06312d64e6b0ca50d54eb6 100644
--- a/tests/edu/caltech/cs2/interfaces/ICollectionTests.java
+++ b/tests/edu/caltech/cs2/interfaces/ICollectionTests.java
@@ -1,7 +1,10 @@
 package edu.caltech.cs2.interfaces;
 
+import edu.caltech.cs2.helpers.DependsOn;
+import edu.caltech.cs2.helpers.TestDescription;
 import edu.caltech.cs2.interfaces.ICollection;
 import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -23,6 +26,8 @@ public interface ICollectionTests {
     @Order(collectionTestLevel)
     @DisplayName("Simple tests of various ICollection functions")
     @ParameterizedTest(name = "Test add(), size(), isEmpty(), contains(), and clear() on [{arguments}]")
+    @DependsOn({"constructor", "iterator", "size", "addBack"})
+    @TestDescription("This test checks the built-in functions we gave you by using your iterator and size methods.")
     @ValueSource(strings = {
             "",
             "1",
@@ -70,6 +75,8 @@ public interface ICollectionTests {
     @Order(collectionTestLevel)
     @Test
     @DisplayName("Test repeated emptying and filling of ICollection with single element")
+    @DependsOn({"constructor", "size", "addBack", "removeBack"})
+    @TestDescription("This test repeatedly adds and clears the data structure.")
     default void testFillEmptyCollection() {
         ICollection<Object> impl = newCollection();
         for (int i = 0; i < 10; i ++) {
@@ -83,6 +90,8 @@ public interface ICollectionTests {
     @Order(collectionTestLevel)
     @DisplayName("Stress test for add(...)")
     @ParameterizedTest(name = "Test add()ing {1} random numbers with seed = {0}")
+    @DependsOn({"constructor", "size", "addBack", "iterator"})
+    @TestDescription("This test adds a ton of random numbers to your data structure and checks that they're all actually in there.")
     @CsvSource({
             "100, 3000", "42, 1000"
     })
@@ -108,6 +117,8 @@ public interface ICollectionTests {
     @Order(collectionTestLevel)
     @DisplayName("Stress test for contains(...)")
     @ParameterizedTest(name = "Test contains() with {1} random numbers and seed = {0}")
+    @DependsOn({"constructor", "size", "addBack", "iterator"})
+    @TestDescription("This test adds a ton of random numbers to your data structure and checks that they're all actually in there.")
     @CsvSource({
             "100, 3000", "42, 1000"
     })
diff --git a/tests/edu/caltech/cs2/interfaces/IDequeTests.java b/tests/edu/caltech/cs2/interfaces/IDequeTests.java
index 591a20b840727457be9609c37122bc980148141e..91dd3d1becbcbc22d6a69cbd3294b5d80011d16a 100644
--- a/tests/edu/caltech/cs2/interfaces/IDequeTests.java
+++ b/tests/edu/caltech/cs2/interfaces/IDequeTests.java
@@ -1,5 +1,8 @@
 package edu.caltech.cs2.interfaces;
 
+import edu.caltech.cs2.helpers.DependsOn;
+import edu.caltech.cs2.helpers.Reflection;
+import edu.caltech.cs2.helpers.TestDescription;
 import org.hamcrest.MatcherAssert;
 import org.hamcrest.collection.IsEmptyIterable;
 import org.junit.jupiter.api.DisplayName;
@@ -18,12 +21,14 @@ import java.util.Random;
 import static edu.caltech.cs2.project03.Project03TestOrdering.*;
 import static org.junit.jupiter.api.Assertions.*;
 
-public interface IDequeTests extends ICollectionTests {
+public interface IDequeTests {
     IDeque<Object> newDeque();
 
     @Order(dequeTestLevel)
     @DisplayName("Test Deque Iterator")
     @ParameterizedTest(name = "Test deque iterator on [{arguments}]")
+    @DependsOn({"constructor", "iterator", "size", "addBack", "removeBack"})
+    @TestDescription("This test checks that your iterator works on various sizes of data structures.")
     @ValueSource(strings = {
             "",
             "1",
@@ -51,6 +56,8 @@ public interface IDequeTests extends ICollectionTests {
 
     @Test
     @Order(dequeTestLevel)
+    @DependsOn({"constructor", "iterator", "size", "addFront", "removeFront"})
+    @TestDescription("This test checks that small deques work correctly when added at the front and removed at the front.")
     @DisplayName("Test Deque addFront / removeFront edge cases")
     default void testRepeatedAddFrontRemoveFront() {
         ArrayDeque<Object> ref = new ArrayDeque<>();
@@ -74,6 +81,8 @@ public interface IDequeTests extends ICollectionTests {
     @Test
     @Order(dequeTestLevel)
     @DisplayName("Test Deque addFront / removeBack edge cases")
+    @DependsOn({"constructor", "iterator", "size", "addFront", "removeBack"})
+    @TestDescription("This test checks that small deques work correctly when added at the front and removed at the back.")
     default void testRepeatedAddFrontRemoveBack() {
         ArrayDeque<Object> ref = new ArrayDeque<>();
         IDeque<Object> impl = newDeque();
@@ -96,6 +105,8 @@ public interface IDequeTests extends ICollectionTests {
     @Test
     @Order(dequeTestLevel)
     @DisplayName("Test Deque addBack / removeFront edge cases")
+    @DependsOn({"constructor", "iterator", "size", "addBack", "removeFront"})
+    @TestDescription("This test checks that small deques work correctly when added at the back and removed at the front.")
     default void testRepeatedAddBackRemoveFront() {
         ArrayDeque<Object> ref = new ArrayDeque<>();
         IDeque<Object> impl = newDeque();
@@ -117,6 +128,8 @@ public interface IDequeTests extends ICollectionTests {
 
     @Test
     @Order(dequeTestLevel)
+    @DependsOn({"constructor", "iterator", "size", "addBack", "removeBack"})
+    @TestDescription("This test checks that small deques work correctly when added at the back and removed at the back.")
     @DisplayName("Test Deque addBack / removeBack edge cases")
     default void testRepeatedAddBackRemoveBack() {
         ArrayDeque<Object> ref = new ArrayDeque<>();
@@ -139,6 +152,8 @@ public interface IDequeTests extends ICollectionTests {
     @Order(dequeTestLevel)
     @DisplayName("Stress test for addFront(...) and peekFront(...)")
     @ParameterizedTest(name = "Test addFront()ing {1} random numbers with seed = {0}")
+    @DependsOn({"constructor", "iterator", "size", "peekFront", "addFront", "toString"})
+    @TestDescription("This test checks uses large amounts of random data to test that your adds work correctly.")
     @CsvSource({
             "100, 300", "42, 500"
     })
@@ -161,6 +176,8 @@ public interface IDequeTests extends ICollectionTests {
 
     @Order(dequeTestLevel)
     @DisplayName("Stress test for addBack(...) and peekBack(...)")
+    @DependsOn({"constructor", "iterator", "size", "peekBack", "addBack", "toString"})
+    @TestDescription("This test checks uses large amounts of random data to test that your adds work correctly.")
     @ParameterizedTest(name = "Test addBack()ing {1} random numbers with seed = {0}")
     @CsvSource({
             "100, 300", "42, 500"
@@ -185,6 +202,8 @@ public interface IDequeTests extends ICollectionTests {
     @Order(dequeTestLevel)
     @DisplayName("Stress test for removeFront(...)")
     @ParameterizedTest(name = "Test removeFront()ing {1} random numbers with seed = {0}")
+    @DependsOn({"constructor", "iterator", "size", "peekFront", "addFront", "removeFront", "toString"})
+    @TestDescription("This test checks uses large amounts of random data to test that your adds and removes work correctly.")
     @CsvSource({
             "101, 300", "45, 500"
     })
@@ -211,6 +230,8 @@ public interface IDequeTests extends ICollectionTests {
     @Order(dequeTestLevel)
     @DisplayName("Stress test for removeBack(...)")
     @ParameterizedTest(name = "Test removeBack()ing {1} random numbers with seed = {0}")
+    @DependsOn({"constructor", "iterator", "size", "peekBack", "addBack", "removeBack", "toString"})
+    @TestDescription("This test checks uses large amounts of random data to test that your adds and removes work correctly.")
     @CsvSource({
             "101, 300", "45, 500"
     })
@@ -237,6 +258,8 @@ public interface IDequeTests extends ICollectionTests {
     @Order(dequeTestLevel)
     @DisplayName("Stress test full IDeque")
     @ParameterizedTest(name = "Test all IDeque methods {1} random numbers with seed = {0}")
+    @DependsOn({"everything"})
+    @TestDescription("This test checks uses large amounts of random data to test that all your methods in the deque work correctly.")
     @CsvSource({
             "102, 300", "52, 500"
     })
@@ -278,6 +301,8 @@ public interface IDequeTests extends ICollectionTests {
     @Order(dequeTestLevel)
     @DisplayName("Test for addAll(...)")
     @ParameterizedTest(name = "Test addAll with {1} random numbers and seed = {0}")
+    @DependsOn({"constructor", "iterator", "addBack", "toString"})
+    @TestDescription("This test checks that the built-in addAll method works using your implementation of the iterator and addBack.")
     @CsvSource({
             "99, 300", "48, 500"
     })
@@ -295,4 +320,21 @@ public interface IDequeTests extends ICollectionTests {
 
         MatcherAssert.assertThat("IDeque has incorrect elements / order", impl, IsIterableContainingInOrder.contains(expected));
     }
+
+    @Order(toStringTestLevel)
+    @DisplayName("toString() matches java.util.ArrayDeque")
+    @DependsOn({"fields", "constructors", "toString", "addBack"})
+    @ParameterizedTest(name = "Test toString() on [{arguments}]")
+    @ValueSource(strings = {
+            "0, 1, 2, 3", "5, 4, 3, 2, 1", "8, 3, 5, 7, 4, 3, 12, 12, 1"
+    })
+    default void testToString(String inputs) {
+        java.util.ArrayDeque<String> reference = new java.util.ArrayDeque<String>();
+        edu.caltech.cs2.interfaces.IDeque<Object> me = newDeque();
+        for (String value : inputs.trim().split(", ")) {
+            assertEquals(reference.toString(), me.toString(), "toString outputs should be the same");
+            reference.addLast(value);
+            me.addBack(value);
+        }
+    }
 }
diff --git a/tests/edu/caltech/cs2/interfaces/IFixedSizeQueueTests.java b/tests/edu/caltech/cs2/interfaces/IFixedSizeQueueTests.java
index b60b17f64187891063878bec2e07f6e54d0d7fde..ce121fac0c39c2aa7d0100e8d4774b1f3f470173 100644
--- a/tests/edu/caltech/cs2/interfaces/IFixedSizeQueueTests.java
+++ b/tests/edu/caltech/cs2/interfaces/IFixedSizeQueueTests.java
@@ -2,6 +2,7 @@ package edu.caltech.cs2.interfaces;
 
 import edu.caltech.cs2.datastructures.CircularArrayFixedSizeQueue;
 import edu.caltech.cs2.helpers.Reflection;
+import edu.caltech.cs2.helpers.TestHint;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Order;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -19,6 +20,7 @@ public interface IFixedSizeQueueTests extends IQueueTests {
 
     @Order(fixedSizeQueueLevel)
     @DisplayName("Overflow test for enqueue(...)")
+    @TestHint("This test is likely failing because you don't have correct wrap-around behavior.\n     Remember that everything should advance; so, it's possible that your start wraps around.")
     @ParameterizedTest(name = "Test randomly enqueue()ing/dequeue()ing {1} random numbers with seed = {0} and fixed array size = {2}")
     @CsvSource({
             "97, 3000, 100", "38, 5000, 10"
diff --git a/tests/edu/caltech/cs2/interfaces/IStyleTests.java b/tests/edu/caltech/cs2/interfaces/IStyleTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..a220fbc8ecd90a79d59c0914e01c3ced6ca74ec0
--- /dev/null
+++ b/tests/edu/caltech/cs2/interfaces/IStyleTests.java
@@ -0,0 +1,88 @@
+package edu.caltech.cs2.interfaces;
+
+import edu.caltech.cs2.helpers.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static edu.caltech.cs2.project03.Project03TestOrdering.classSpecificTestLevel;
+
+public interface IStyleTests {
+    String getSource();
+    Class<?> getClazz();
+    List<String> getPublicInterface();
+    int getMaxFields();
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("The overall number of fields is small")
+    @TestHint("It's good style to use the smallest number of fields that you can in each class.")
+    @Test
+    default void testSmallNumberOfFields() {
+        Reflection.assertFieldsLessThan(getClazz(), "private", getMaxFields());
+    }
+
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("The public interface is correct")
+    @TestDescription("This test checks that you haven't declared any extra public methods.")
+    @TestHint("You are welcome to make any extra methods you want, but they have to be private!")
+    @Test
+    default void testPublicInterface() {
+        Reflection.assertPublicInterface(getClazz(), getPublicInterface());
+    }
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("Does not use or import disallowed classes")
+    @TestHint("Remember that you're not allowed to use anything in java.util except Iterator and Random!")
+    @Test
+    default void testForInvalidClasses() {
+        List<String> regexps = List.of("java\\.util\\.(?!Iterator|Random|function.Function)", "java\\.lang\\.reflect", "java\\.io");
+        Inspection.assertNoImportsOf(getSource(), regexps);
+        Inspection.assertNoUsageOf(getSource(), regexps);
+    }
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("Does not attempt to get around constructor counts")
+    @Test
+    default void testForAvoidCounters() {
+        List<String> regexps = List.of("NUM_CALLS");
+        Inspection.assertNoUsageOf(getSource(), regexps);
+    }
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("Uses this(...) notation in all but one constructor")
+    @TestDescription("This test is checking that all of your constructors except one call the other constructors rather than duplicating code.")
+    @Test
+    default void testForThisConstructors() {
+        Inspection.assertConstructorHygiene(getSource());
+    }
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("All fields have modifiers")
+    @TestDescription("This test checks that every field is private or public.")
+    @Test
+    default void testFieldModifiers() {
+        Reflection.assertFieldModifiers(getClazz());
+    }
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("There are no public fields")
+    @TestHint("It's good style to never use public fields whenever avoidable.")
+    @Test
+    default void testNoPublicFields() {
+        Reflection.assertNoPublicFields(getClazz());
+    }
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("There are no protected fields")
+    @TestHint("It's good style to never use protected fields whenever avoidable.")
+    @Test
+    default void testNoProtectedFields() {
+        Reflection.assertNoProtectedFields(getClazz());
+    }
+
+
+
+}
\ No newline at end of file
diff --git a/tests/edu/caltech/cs2/project03/GuitarStringTests.java b/tests/edu/caltech/cs2/project03/GuitarStringTests.java
index 81fabe06152bac315ec51b37b2778c18d5464f64..bc812b198c0d0d79741b6b9316a0a4d96bc93bb7 100644
--- a/tests/edu/caltech/cs2/project03/GuitarStringTests.java
+++ b/tests/edu/caltech/cs2/project03/GuitarStringTests.java
@@ -1,13 +1,15 @@
 package edu.caltech.cs2.project03;
 
-import edu.caltech.cs2.datastructures.CircularArrayFixedSizeQueue;
-import edu.caltech.cs2.helpers.Inspection;
 import edu.caltech.cs2.helpers.Reflection;
+import edu.caltech.cs2.helpers.TestDescription;
+import edu.caltech.cs2.helpers.TestExtension;
+import edu.caltech.cs2.helpers.TestHint;
 import edu.caltech.cs2.interfaces.IFixedSizeQueue;
+import edu.caltech.cs2.interfaces.IStyleTests;
 import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.CsvSource;
-import org.junit.jupiter.params.provider.ValueSource;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -17,7 +19,6 @@ import java.util.List;
 import java.util.Random;
 import java.util.Scanner;
 import java.util.stream.Stream;
-import java.util.function.Function;
 
 import static edu.caltech.cs2.project03.Project03TestOrdering.*;
 import static java.lang.Math.abs;
@@ -26,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @Tag("A")
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@ExtendWith(TestExtension.class)
 public class GuitarStringTests {
   private static String STRING_SOURCE = "src/edu/caltech/cs2/project03/CircularArrayFixedSizeQueueGuitarString.java";
 
@@ -36,203 +38,180 @@ public class GuitarStringTests {
 
   public static IFixedSizeQueue<Double> getQueueFromString(CircularArrayFixedSizeQueueGuitarString string) {
     String queueName = Reflection.getFieldByType(CircularArrayFixedSizeQueueGuitarString.class, IFixedSizeQueue.class)
-        .getName();
+            .getName();
     return Reflection.getFieldValue(CircularArrayFixedSizeQueueGuitarString.class, queueName, string);
   }
+  @DisplayName("Style")
+  @Nested
+  class StyleTests implements IStyleTests {
+
+    @Order(classSpecificTestLevel)
+    @DisplayName("There are three static fields: the two double constants and a random value generator")
+    @TestDescription("This test is checking that you have three constant fields for the \"magic numbers\" and the Random instance.")
+    @Test
+    public void testStaticFields() {
+      Reflection.assertFieldsEqualTo(CircularArrayFixedSizeQueueGuitarString.class, "static", 3);
+      Stream<Field> fields = Reflection.getFields(CircularArrayFixedSizeQueueGuitarString.class);
+      fields.filter(Reflection.hasModifier("static")).forEach((field) -> {
+        Reflection.checkFieldModifiers(field, List.of("private", "static"));
+        assertTrue(Reflection.hasModifier("final").test(field) || field.getType().equals(Random.class),
+                "non-final static class must be a random value generator");
+      });
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("Does not use or import disallowed classes")
-  @Test
-  public void testForInvalidClasses() {
-    List<String> regexps = List.of("java\\.util\\.(?!Random|function.Function)", "java\\.lang\\.reflect", "java\\.io");
-    Inspection.assertNoImportsOf(STRING_SOURCE, regexps);
-    Inspection.assertNoUsageOf(STRING_SOURCE, regexps);
-  }
-
-  @Order(classSpecificTestLevel)
-  @DisplayName("Uses this(...) notation in all but one constructor")
-  @Test
-  public void testForThisConstructors() {
-    Inspection.assertConstructorHygiene(STRING_SOURCE);
-  }
-
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are three static fields: the two double constants and a random value generator")
-  @Test
-  public void testStaticFields() {
-    Reflection.assertFieldsEqualTo(CircularArrayFixedSizeQueueGuitarString.class, "static", 3);
-    Stream<Field> fields = Reflection.getFields(CircularArrayFixedSizeQueueGuitarString.class);
-    fields.filter(Reflection.hasModifier("static")).forEach((field) -> {
-      Reflection.checkFieldModifiers(field, List.of("private", "static"));
-      assertTrue(Reflection.hasModifier("final").test(field) || field.getType().equals(Random.class),
-          "non-final static class must be a random value generator");
-    });
-  }
-
-  @Order(classSpecificTestLevel)
-  @DisplayName("The overall number of fields is small")
-  @Test
-  public void testSmallNumberOfFields() {
-    Reflection.assertFieldsLessThan(CircularArrayFixedSizeQueueGuitarString.class, "private", 5);
-  }
-
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no public fields")
-  @Test
-  public void testNoPublicFields() {
-    Reflection.assertNoPublicFields(CircularArrayFixedSizeQueueGuitarString.class);
-  }
-
-  @Order(classSpecificTestLevel)
-  @DisplayName("There are no protected fields")
-  @Test
-  public void testNoProtectedFields() {
-    Reflection.assertNoProtectedFields(CircularArrayFixedSizeQueueGuitarString.class);
-  }
-
-  @Order(classSpecificTestLevel)
-  @DisplayName("All fields in CircularArrayFixedSizeQueueGuitarString have modifiers")
-  @Test
-  public void testFieldModifiers() {
-    Reflection.assertFieldModifiers(CircularArrayFixedSizeQueueGuitarString.class);
-  }
-
-  @Order(classSpecificTestLevel)
-  @DisplayName("The public interface is correct")
-  @Test
-  public void testPublicInterface() {
-    Reflection.assertPublicInterface(CircularArrayFixedSizeQueueGuitarString.class,
-        List.of("length", "pluck", "tic", "sample"));
-  }
+    public int getMaxFields() {
+      return 3;
+    }
 
-  @Order(classSpecificTestLevel)
-  @DisplayName("The constructor correctly sets up the queue")
-  @ParameterizedTest(name = "Test constructor with CircularArrayFixedSizeQueue and a frequency of {0} Hz; expected queue size is {1}")
-  @CsvSource({ "110, 401", "340, 130", "512, 87", "600.5, 74", "880, 51" })
-  public void testConstructor(double frequency, int expectedSize) {
-    CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
-    IFixedSizeQueue<Double> queue = getQueueFromString(string);
-
-    assertEquals(expectedSize, queue.size(), "Queue size is not equal to expected size");
-    for (double val : queue) {
-      assertEquals(0, val, "All values in queue should be equal to 0");
+    public List<String> getPublicInterface() {
+      return List.of("length", "pluck", "tic", "sample");
     }
-  }
 
-  @Order(guitarStringTestLevel)
-  @DisplayName("The pluck() method randomizes the values in the queue")
-  @ParameterizedTest(name = "Test pluck() with CircularArrayFixedSizeQueue and a frequency of {0} Hz")
-  @CsvSource({ "100", "50", "10", "8", "5" })
-  public void testPluck(double frequency) {
-    final double DELTA = 0.05;
-    // Set up class and retrieve queue
-    CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
-    IFixedSizeQueue<Double> queue = getQueueFromString(string);
-    // Checks all values are initially 0
-    for (double val : queue) {
-      assertEquals(0, val, "initial values must be 0");
+    @Override
+    public String getSource() {
+      return STRING_SOURCE;
     }
-    string.pluck();
-    queue = getQueueFromString(string);
-    double sum = 0;
-    double absSum = 0;
-    for (double val : queue) {
-      sum += val;
-      absSum += abs(val);
+
+    @Override
+    public Class<?> getClazz() {
+      return CircularArrayFixedSizeQueueGuitarString.class;
     }
-    assertEquals(0, sum / queue.size(), DELTA, "average value of uniform distribution should be near 0");
-    assertEquals(0.25, absSum / queue.size(), DELTA, "average magnitude of uniform distribution should be near 0.25");
   }
 
-  @Order(guitarStringTestLevel)
-  @DisplayName("The tic() method correctly applies the Karplus-Strong algorithm")
-  @ParameterizedTest(name = "Test tic() with CircularArrayFixedSizeQueue and a frequency of {0} Hz; data file {1}.txt")
-  @CsvSource({ "10000, ticStates1", "8000, ticStates2", "5000, ticStates3" })
-  public void testTic(double frequency, String filename) {
-    // Set up scanner
-    String filepath = "tests/data/" + filename + ".txt";
-    Scanner in;
-    try {
-      in = new Scanner(new File(filepath));
-    } catch (FileNotFoundException e) {
-      throw new IllegalArgumentException(filepath + " is not a valid trace file.");
+  @DisplayName("Implementation Tests")
+  @Nested
+  class ImplementationTests {
+    @Order(classSpecificTestLevel)
+    @DisplayName("The constructor correctly sets up the queue")
+    @TestHint("Make sure that your queue is the correct size according to the specification and filled with zeroes.")
+    @ParameterizedTest(name = "Test constructor with CircularArrayFixedSizeQueue and a frequency of {0} Hz; expected queue size is {1}")
+    @CsvSource({"110, 401", "340, 130", "512, 87", "600.5, 74", "880, 51"})
+    public void testConstructor(double frequency, int expectedSize) {
+      CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
+      IFixedSizeQueue<Double> queue = getQueueFromString(string);
+
+      assertEquals(expectedSize, queue.size(), "Queue size is not equal to expected size");
+      for (double val : queue) {
+        assertEquals(0, val, "All values in queue should be equal to 0");
+      }
     }
-    // Set up class and retrieve queue
-    CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
-    IFixedSizeQueue<Double> queue = getQueueFromString(string);
-    // Reinitialize queue with new data
-    for (int i = 0; i < queue.size(); i++) {
-      queue.dequeue();
-      queue.enqueue(in.nextDouble());
-    }
-    int initSize = queue.size();
-    // Pass through the same number of tics as elements in the array
-    for (int i = 0; i < initSize; i++) {
-      string.tic();
+
+    @Order(guitarStringTestLevel)
+    @DisplayName("The pluck() method randomizes the values in the queue")
+    @ParameterizedTest(name = "Test pluck() with CircularArrayFixedSizeQueue and a frequency of {0} Hz")
+    @TestHint("Make sure that you're centering your distribution around the correct number.")
+    @CsvSource({"100", "50", "10", "8", "5"})
+    public void testPluck(double frequency) {
+      final double DELTA = 0.05;
+      // Set up class and retrieve queue
+      CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
+      IFixedSizeQueue<Double> queue = getQueueFromString(string);
+      // Checks all values are initially 0
+      for (double val : queue) {
+        assertEquals(0, val, "initial values must be 0");
+      }
+      string.pluck();
       queue = getQueueFromString(string);
-      assertEquals(initSize, queue.size(), "queue size must remain the same");
+      double sum = 0;
+      double absSum = 0;
+      for (double val : queue) {
+        sum += val;
+        absSum += abs(val);
+      }
+      assertEquals(0, sum / queue.size(), DELTA, "average value of uniform distribution should be near 0");
+      assertEquals(0.25, absSum / queue.size(), DELTA, "average magnitude of uniform distribution should be near 0.25");
     }
-    // Compare peek() values with the expected values in the files
-    while (in.hasNext()) {
-      string.tic();
-      queue = getQueueFromString(string);
-      assertEquals(initSize, queue.size(), "queue size must remain the same");
-      assertEquals(in.nextDouble(), queue.peek(), "next expected value not at front of queue");
+
+    @Order(guitarStringTestLevel)
+    @DisplayName("The tic() method correctly applies the Karplus-Strong algorithm")
+    @ParameterizedTest(name = "Test tic() with CircularArrayFixedSizeQueue and a frequency of {0} Hz; data file {1}.txt")
+    @CsvSource({"10000, ticStates1", "8000, ticStates2", "5000, ticStates3"})
+    public void testTic(double frequency, String filename) {
+      // Set up scanner
+      String filepath = "tests/data/" + filename + ".txt";
+      Scanner in;
+      try {
+        in = new Scanner(new File(filepath));
+      } catch (FileNotFoundException e) {
+        throw new IllegalArgumentException(filepath + " is not a valid trace file.");
+      }
+      // Set up class and retrieve queue
+      CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
+      IFixedSizeQueue<Double> queue = getQueueFromString(string);
+      // Reinitialize queue with new data
+      for (int i = 0; i < queue.size(); i++) {
+        queue.dequeue();
+        queue.enqueue(in.nextDouble());
+      }
+      int initSize = queue.size();
+      // Pass through the same number of tics as elements in the array
+      for (int i = 0; i < initSize; i++) {
+        string.tic();
+        queue = getQueueFromString(string);
+        assertEquals(initSize, queue.size(), "queue size must remain the same");
+      }
+      // Compare peek() values with the expected values in the files
+      while (in.hasNext()) {
+        string.tic();
+        queue = getQueueFromString(string);
+        assertEquals(initSize, queue.size(), "queue size must remain the same");
+        assertEquals(in.nextDouble(), queue.peek(), "next expected value not at front of queue");
+      }
     }
-  }
 
-  @Order(guitarStringTestLevel)
-  @DisplayName("The length() method correctly gives the length of the queue")
-  @ParameterizedTest(name = "Test length() with CircularArrayFixedSizeQueue and a frequency of {0} Hz; expected length = {1}; iterations = {2}")
-  @CsvSource({ "110, 401, 1000", "340, 130, 500", "512, 87, 200", "600.5, 74, 150", "880, 51, 100" })
-  public void testLength(double frequency, int expectedLength, int iterations) {
-    // Set up class and retrieve queue
-    CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
-    IFixedSizeQueue<Double> queue = getQueueFromString(string);
-
-    // Pluck and make sure length doesn't change
-    int initSize = queue.size();
-    assertEquals(expectedLength, string.length(), "Length should be same as expected");
-    assertEquals(queue.size(), string.length(), "Length should be same as queue size");
-    string.pluck();
-    queue = getQueueFromString(string);
-    assertEquals(initSize, string.length(), "Length should not have changed from beginning");
-    assertEquals(queue.size(), string.length(), "Length should be same as queue size");
-
-    // Run through many iterations, making sure both the queue size and length are
-    // constant
-    for (int i = 0; i < iterations; i++) {
-      string.tic();
+    @Order(guitarStringTestLevel)
+    @DisplayName("The length() method correctly gives the length of the queue")
+    @ParameterizedTest(name = "Test length() with CircularArrayFixedSizeQueue and a frequency of {0} Hz; expected length = {1}; iterations = {2}")
+    @CsvSource({"110, 401, 1000", "340, 130, 500", "512, 87, 200", "600.5, 74, 150", "880, 51, 100"})
+    public void testLength(double frequency, int expectedLength, int iterations) {
+      // Set up class and retrieve queue
+      CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
+      IFixedSizeQueue<Double> queue = getQueueFromString(string);
+
+      // Pluck and make sure length doesn't change
+      int initSize = queue.size();
+      assertEquals(expectedLength, string.length(), "Length should be same as expected");
+      assertEquals(queue.size(), string.length(), "Length should be same as queue size");
+      string.pluck();
       queue = getQueueFromString(string);
       assertEquals(initSize, string.length(), "Length should not have changed from beginning");
       assertEquals(queue.size(), string.length(), "Length should be same as queue size");
-    }
 
-  }
+      // Run through many iterations, making sure both the queue size and length are
+      // constant
+      for (int i = 0; i < iterations; i++) {
+        string.tic();
+        queue = getQueueFromString(string);
+        assertEquals(initSize, string.length(), "Length should not have changed from beginning");
+        assertEquals(queue.size(), string.length(), "Length should be same as queue size");
+      }
+
+    }
 
-  @Order(guitarStringTestLevel)
-  @DisplayName("The sample() method gives the same values as peek()ing the queue")
-  @ParameterizedTest(name = "Test sample() with CircularArrayFixedSizeQueue and a frequency of {0} Hz")
-  @CsvSource({ "110, 1000", "340, 500", "512, 200", "600.5, 150", "880, 100" })
-  public void testSample(double frequency, int iterations) {
-    // Set up class and retrieve queue
-    CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
-    IFixedSizeQueue<Double> queue = getQueueFromString(string);
-
-    // Pluck and make sure initial samples are correct
-    assertEquals(0, string.sample(), "Sample should return 0 before plucking");
-    assertEquals(queue.peek(), string.sample(), "Sample should same as peek()ing queue");
-    string.pluck();
-    queue = getQueueFromString(string);
-    assertEquals(queue.peek(), string.sample(), "Sample should same as peek()ing queue");
-
-    // Run through many iterations, making sure sample() matches peek()
-    for (int i = 0; i < iterations; i++) {
-      string.tic();
+    @Order(guitarStringTestLevel)
+    @DisplayName("The sample() method gives the same values as peek()ing the queue")
+    @ParameterizedTest(name = "Test sample() with CircularArrayFixedSizeQueue and a frequency of {0} Hz")
+    @CsvSource({"110, 1000", "340, 500", "512, 200", "600.5, 150", "880, 100"})
+    public void testSample(double frequency, int iterations) {
+      // Set up class and retrieve queue
+      CircularArrayFixedSizeQueueGuitarString string = constructGuitarString(frequency);
+      IFixedSizeQueue<Double> queue = getQueueFromString(string);
+
+      // Pluck and make sure initial samples are correct
+      assertEquals(0, string.sample(), "Sample should return 0 before plucking");
+      assertEquals(queue.peek(), string.sample(), "Sample should same as peek()ing queue");
+      string.pluck();
       queue = getQueueFromString(string);
       assertEquals(queue.peek(), string.sample(), "Sample should same as peek()ing queue");
-    }
 
+      // Run through many iterations, making sure sample() matches peek()
+      for (int i = 0; i < iterations; i++) {
+        string.tic();
+        queue = getQueueFromString(string);
+        assertEquals(queue.peek(), string.sample(), "Sample should same as peek()ing queue");
+      }
+
+    }
   }
 
 }