public static <W extends BoundedWindow, AccumT, OutputT>
      TriggerTester<Integer, OutputT, W> combining(
          WindowFn<?, W> windowFn,
          Trigger<W> trigger,
          AccumulationMode mode,
          KeyedCombineFn<String, Integer, AccumT, OutputT> combineFn,
          Coder<OutputT> outputCoder,
          Duration allowedDataLateness)
          throws Exception {

    WindowingStrategy<?, W> strategy =
        WindowingStrategy.of(windowFn)
            .withTrigger(trigger)
            .withMode(mode)
            .withAllowedLateness(allowedDataLateness);

    CoderRegistry registry = new CoderRegistry();
    registry.registerStandardCoders();
    AppliedCombineFn<String, Integer, AccumT, OutputT> fn =
        AppliedCombineFn.<String, Integer, AccumT, OutputT>withInputCoder(
            combineFn, registry, KvCoder.of(StringUtf8Coder.of(), VarIntCoder.of()));

    return new TriggerTester<Integer, OutputT, W>(
        strategy,
        SystemReduceFn.<String, Integer, AccumT, OutputT, W>combining(StringUtf8Coder.of(), fn)
            .create(KEY),
        outputCoder);
  }
  public static boolean isSupported(WindowingStrategy<?, ?> strategy) {
    // TODO: Add support for other triggers.
    if (!(strategy.getTrigger().getSpec() instanceof DefaultTrigger)) {
      return false;
    }

    // Right now, we support ACCUMULATING_FIRED_PANES because it is the same as
    // DISCARDING_FIRED_PANES. In Batch mode there is no late data so the default
    // trigger (after watermark) will only fire once.
    if (!strategy.getMode().equals(AccumulationMode.DISCARDING_FIRED_PANES)
        && !strategy.getMode().equals(AccumulationMode.ACCUMULATING_FIRED_PANES)) {
      return false;
    }

    return true;
  }
  private TriggerTester(
      WindowingStrategy<?, W> wildcardStrategy,
      ReduceFn<String, InputT, OutputT, W> reduceFn,
      Coder<OutputT> outputCoder)
      throws Exception {
    @SuppressWarnings("unchecked")
    WindowingStrategy<Object, W> objectStrategy = (WindowingStrategy<Object, W>) wildcardStrategy;

    this.windowFn = objectStrategy.getWindowFn();
    this.stubContexts = new StubContexts();
    this.outputCoder = outputCoder;
    executableTrigger = wildcardStrategy.getTrigger();

    this.runner =
        new ReduceFnRunner<>(
            KEY,
            objectStrategy,
            timerInternals,
            stubContexts,
            droppedDueToClosedWindow,
            droppedDueToLateness,
            reduceFn);
  }
  public static <W extends BoundedWindow> TriggerTester<Integer, Iterable<Integer>, W> nonCombining(
      WindowFn<?, W> windowFn,
      Trigger<W> trigger,
      AccumulationMode mode,
      Duration allowedDataLateness)
      throws Exception {

    WindowingStrategy<?, W> strategy =
        WindowingStrategy.of(windowFn)
            .withTrigger(trigger)
            .withMode(mode)
            .withAllowedLateness(allowedDataLateness);
    return nonCombining(strategy);
  }
  protected <T> WindowedValue<T> makeWindowedValue(
      T output, Instant timestamp, Collection<? extends BoundedWindow> windows, PaneInfo pane) {
    final Instant inputTimestamp = timestamp;
    final WindowFn windowFn = windowingStrategy.getWindowFn();

    if (timestamp == null) {
      timestamp = BoundedWindow.TIMESTAMP_MIN_VALUE;
    }

    if (windows == null) {
      try {
        windows =
            windowFn.assignWindows(
                windowFn.new AssignContext() {
                  @Override
                  public Object element() {
                    throw new UnsupportedOperationException(
                        "WindowFn attempted to access input element when none was available"); // TODO: 12/16/15 aljoscha's comment in slack
                  }

                  @Override
                  public Instant timestamp() {
                    if (inputTimestamp == null) {
                      throw new UnsupportedOperationException(
                          "WindowFn attempted to access input timestamp when none was available");
                    }
                    return inputTimestamp;
                  }

                  @Override
                  public Collection<? extends BoundedWindow> windows() {
                    throw new UnsupportedOperationException(
                        "WindowFn attempted to access input windows when none were available");
                  }
                });
      } catch (Exception e) {
        Throwables.propagateIfInstanceOf(e, UserCodeException.class);
        throw new UserCodeException(e);
      }
    }

    return WindowedValue.of(output, timestamp, windows, pane);
  }