public static <FnT extends DoFn<?, ?>> DoFnSignature getSignature(Class<FnT> fn) {
    return signatureCache.computeIfAbsent(fn, DoFnSignatures::parseSignature);
  }
@Test
  public void testProcessElementWithOnTimerContextRejected() throws Exception {
    thrown.expect(IllegalArgumentException.class);
    // The message should at least mention @ProcessElement and OnTimerContext
    thrown.expectMessage("@" + DoFn.ProcessElement.class.getSimpleName());
    thrown.expectMessage(DoFn.OnTimerContext.class.getSimpleName());

    DoFnSignatures.getSignature(
        new DoFn<KV<String, Integer>, Long>() {

          @TimerId("foo")
          private final TimerSpec bizzle = TimerSpecs.timer(TimeDomain.EVENT_TIME);

          @ProcessElement
          public void foo(ProcessContext context, OnTimerContext bogus) {}

          @OnTimer("foo")
          public void onFoo() {}
        }.getClass());
  }