@Override
  public boolean isReady(PCollectionView<?> sideInput, BoundedWindow window) {
    Set<BoundedWindow> readyWindows =
        stateInternals.state(StateNamespaces.global(), availableWindowsTags.get(sideInput)).read();

    return readyWindows != null && readyWindows.contains(window);
  }
@Test
  public void testIsReady() {
    long view1WindowSize = 100;
    PCollection<String> pc = Pipeline.create().apply(Create.of("1"));
    PCollectionView<Iterable<String>> view1 =
        pc.apply(Window.into(FixedWindows.of(Duration.millis(view1WindowSize))))
            .apply(View.asIterable());

    // Unused, just to have a non-trivial handler set up
    PCollectionView<Iterable<String>> view2 = pc.apply(View.asIterable());

    SideInputHandler sideInputHandler =
        new SideInputHandler(
            ImmutableList.of(view1, view2), InMemoryStateInternals.<Void>forKey(null));

    // Adjacent fixed windows
    IntervalWindow firstWindow = new IntervalWindow(new Instant(0), new Instant(view1WindowSize));
    IntervalWindow secondWindow =
        new IntervalWindow(new Instant(view1WindowSize), new Instant(view1WindowSize * 2));

    // side input should not yet be ready in first window
    assertFalse(sideInputHandler.isReady(view1, firstWindow));

    // add a value for view1
    sideInputHandler.addSideInputValue(
        view1,
        valuesInWindow(
            materializeValuesFor(view1.getPipeline().getOptions(), View.asIterable(), "Hello"),
            new Instant(0),
            firstWindow));

    // now side input should be ready in first window
    assertTrue(sideInputHandler.isReady(view1, firstWindow));

    // second window input should still not be ready
    assertFalse(sideInputHandler.isReady(view1, secondWindow));
  }