/**
   * Prepare the original diamond-dependency problem, where B depends on A and C depends on both A
   * and B. We need to guarantee that C can read all of its dependencies only when they're in a
   * 'consistent' state, which is when they've received all events from their dependencies, and
   * their dependencies are in a consistent state.
   *
   * <p>A --> B --. This diagram shows A --> B, B --> C and A --> C. | | The only safe notification
   * order of events is | V for B to receive events from A before C receives '-------> C those same
   * events.
   */
  public void testDiamondDependency() {

    SequenceDependenciesEventPublisher publisher = new SequenceDependenciesEventPublisher();
    DependentSubjectListener a = new DependentSubjectListener("A", publisher);
    DependentSubjectListener b = new DependentSubjectListener("B", publisher);
    DependentSubjectListener c = new DependentSubjectListener("C", publisher);

    a.addListener(c);
    a.increment(10);
    assertEquals(10, a.latestRevision);
    assertEquals(0, b.latestRevision);
    assertEquals(10, c.latestRevision);

    c.increment(5);
    assertEquals(10, a.latestRevision);
    assertEquals(0, b.latestRevision);
    assertEquals(15, c.latestRevision);

    b.increment(20);
    assertEquals(10, a.latestRevision);
    assertEquals(20, b.latestRevision);
    assertEquals(15, c.latestRevision);

    b.addListener(c);
    assertEquals(10, a.latestRevision);
    assertEquals(20, b.latestRevision);
    assertEquals(20, c.latestRevision);

    b.increment(2);
    assertEquals(10, a.latestRevision);
    assertEquals(22, b.latestRevision);
    assertEquals(22, c.latestRevision);

    a.increment(15);
    assertEquals(25, a.latestRevision);
    assertEquals(22, b.latestRevision);
    assertEquals(25, c.latestRevision);

    a.addListener(b);
    assertEquals(25, a.latestRevision);
    assertEquals(25, b.latestRevision);
    assertEquals(25, c.latestRevision);

    a.increment(4);
    assertEquals(29, a.latestRevision);
    assertEquals(29, b.latestRevision);
    assertEquals(29, c.latestRevision);
  }
  /**
   * The publisher should throw an IllegalStateException when a cycle in the listener graph is
   * created.
   */
  public void testCycleThrows() {
    SequenceDependenciesEventPublisher publisher = new SequenceDependenciesEventPublisher();
    DependentSubjectListener a = new DependentSubjectListener("A", publisher);
    DependentSubjectListener b = new DependentSubjectListener("B", publisher);
    DependentSubjectListener c = new DependentSubjectListener("C", publisher);

    // simple cycle of three
    a.addListener(b);
    b.addListener(c);
    try {
      c.addListener(a);
      fail("Cycle not detected");
    } catch (IllegalStateException e) {
      // expected
    }

    // cycle of 10
    publisher = new SequenceDependenciesEventPublisher();
    DependentSubjectListener[] subjects = new DependentSubjectListener[10];
    for (int i = 0; i < 10; i++) {
      subjects[i] = new DependentSubjectListener("" + i, publisher);
      if (i > 0) subjects[i - 1].addListener(subjects[i]);
    }
    try {
      subjects[9].addListener(subjects[0]);
      fail("Cycle not detected");
    } catch (IllegalStateException e) {
      // expected
    }

    // cycle of 1
    publisher = new SequenceDependenciesEventPublisher();
    a = new DependentSubjectListener("A", publisher);
    try {
      a.addListener(a);
      fail("Cycle not detected");
    } catch (IllegalStateException e) {
      // expected
    }
  }