public static <FnT extends DoFn<?, ?>> DoFnSignature getSignature(Class<FnT> fn) {
    return signatureCache.computeIfAbsent(fn, DoFnSignatures::parseSignature);
  }
@Test
  public void testOnWindowExpirationWithAllowedParams() {
    DoFnSignature sig =
        DoFnSignatures.getSignature(
            new DoFn<String, String>() {

              @StateId("foo")
              private final StateSpec<ValueState<Integer>> bizzle =
                  StateSpecs.value(VarIntCoder.of());

              @ProcessElement
              public void process(ProcessContext c) {}

              @OnWindowExpiration
              public void bar(
                  BoundedWindow b,
                  @StateId("foo") ValueState<Integer> s,
                  PipelineOptions p,
                  OutputReceiver<String> o,
                  MultiOutputReceiver m) {}
            }.getClass());

    List<Parameter> params = sig.onWindowExpiration().extraParameters();
    assertThat(params.size(), equalTo(5));
    assertThat(params.get(0), instanceOf(WindowParameter.class));
    assertThat(params.get(1), instanceOf(StateParameter.class));
    assertThat(params.get(2), instanceOf(PipelineOptionsParameter.class));
    assertThat(params.get(3), instanceOf(OutputReceiverParameter.class));
    assertThat(params.get(4), instanceOf(TaggedOutputReceiverParameter.class));
  }