@Test
  public void testKeyValueStateInWindowFunction() {
    try {

      StatefulFunction.globalCounts.clear();

      // tumbling window that triggers every 20 milliseconds
      AccumulatingProcessingTimeWindowOperator<Integer, Integer, Integer> op =
          new AccumulatingProcessingTimeWindowOperator<>(
              new StatefulFunction(),
              identitySelector,
              IntSerializer.INSTANCE,
              IntSerializer.INSTANCE,
              50,
              50);

      TestTimeServiceProvider timerService = new TestTimeServiceProvider();

      OneInputStreamOperatorTestHarness<Integer, Integer> testHarness =
          new KeyedOneInputStreamOperatorTestHarness<>(
              op,
              new ExecutionConfig(),
              timerService,
              identitySelector,
              BasicTypeInfo.INT_TYPE_INFO);

      testHarness.open();

      timerService.setCurrentTime(0);

      testHarness.processElement(new StreamRecord<>(1));
      testHarness.processElement(new StreamRecord<>(2));

      op.processElement(new StreamRecord<>(1));
      op.processElement(new StreamRecord<>(2));
      op.processElement(new StreamRecord<>(1));
      op.processElement(new StreamRecord<>(1));
      op.processElement(new StreamRecord<>(2));
      op.processElement(new StreamRecord<>(2));

      timerService.setCurrentTime(1000);

      List<Integer> result = extractFromStreamRecords(testHarness.getOutput());
      assertEquals(8, result.size());

      Collections.sort(result);
      assertEquals(Arrays.asList(1, 1, 1, 1, 2, 2, 2, 2), result);

      assertEquals(4, StatefulFunction.globalCounts.get(1).intValue());
      assertEquals(4, StatefulFunction.globalCounts.get(2).intValue());

      testHarness.close();
      op.dispose();
    } catch (Exception e) {
      e.printStackTrace();
      fail(e.getMessage());
    }
  }
  @Test
  public void checkpointRestoreWithPendingWindowSliding() {
    try {
      final int factor = 4;
      final int windowSlide = 50;
      final int windowSize = factor * windowSlide;

      TestTimeServiceProvider timerService = new TestTimeServiceProvider();

      // sliding window (200 msecs) every 50 msecs
      AccumulatingProcessingTimeWindowOperator<Integer, Integer, Integer> op =
          new AccumulatingProcessingTimeWindowOperator<>(
              validatingIdentityFunction,
              identitySelector,
              IntSerializer.INSTANCE,
              IntSerializer.INSTANCE,
              windowSize,
              windowSlide);

      OneInputStreamOperatorTestHarness<Integer, Integer> testHarness =
          new OneInputStreamOperatorTestHarness<>(op, new ExecutionConfig(), timerService);

      timerService.setCurrentTime(0);

      testHarness.setup();
      testHarness.open();

      // inject some elements
      final int numElements = 1000;
      final int numElementsFirst = 700;

      for (int i = 0; i < numElementsFirst; i++) {
        testHarness.processElement(new StreamRecord<>(i));
      }

      // draw a snapshot
      List<Integer> resultAtSnapshot = extractFromStreamRecords(testHarness.getOutput());
      int beforeSnapShot = testHarness.getOutput().size();
      StreamStateHandle state = testHarness.snapshot(1L, System.currentTimeMillis());
      int afterSnapShot = testHarness.getOutput().size();
      assertEquals("operator performed computation during snapshot", beforeSnapShot, afterSnapShot);

      assertTrue(resultAtSnapshot.size() <= factor * numElementsFirst);

      // inject the remaining elements - these should not influence the snapshot
      for (int i = numElementsFirst; i < numElements; i++) {
        testHarness.processElement(new StreamRecord<>(i));
      }

      op.dispose();

      // re-create the operator and restore the state
      op =
          new AccumulatingProcessingTimeWindowOperator<>(
              validatingIdentityFunction,
              identitySelector,
              IntSerializer.INSTANCE,
              IntSerializer.INSTANCE,
              windowSize,
              windowSlide);

      testHarness =
          new OneInputStreamOperatorTestHarness<>(op, new ExecutionConfig(), timerService);

      testHarness.setup();
      testHarness.restore(state);
      testHarness.open();

      // inject again the remaining elements
      for (int i = numElementsFirst; i < numElements; i++) {
        testHarness.processElement(new StreamRecord<>(i));
      }

      timerService.setCurrentTime(50);
      timerService.setCurrentTime(100);
      timerService.setCurrentTime(150);
      timerService.setCurrentTime(200);
      timerService.setCurrentTime(250);
      timerService.setCurrentTime(300);
      timerService.setCurrentTime(350);

      testHarness.close();
      op.dispose();

      // get and verify the result
      List<Integer> finalResult = new ArrayList<>(resultAtSnapshot);
      List<Integer> finalPartialResult = extractFromStreamRecords(testHarness.getOutput());
      finalResult.addAll(finalPartialResult);
      assertEquals(factor * numElements, finalResult.size());

      Collections.sort(finalResult);
      for (int i = 0; i < factor * numElements; i++) {
        assertEquals(i / factor, finalResult.get(i).intValue());
      }
    } catch (Exception e) {
      e.printStackTrace();
      fail(e.getMessage());
    }
  }
  @Test
  public void checkpointRestoreWithPendingWindowTumbling() {
    try {
      final int windowSize = 200;

      // tumbling window that triggers every 200 milliseconds
      AccumulatingProcessingTimeWindowOperator<Integer, Integer, Integer> op =
          new AccumulatingProcessingTimeWindowOperator<>(
              validatingIdentityFunction,
              identitySelector,
              IntSerializer.INSTANCE,
              IntSerializer.INSTANCE,
              windowSize,
              windowSize);

      TestTimeServiceProvider timerService = new TestTimeServiceProvider();

      OneInputStreamOperatorTestHarness<Integer, Integer> testHarness =
          new OneInputStreamOperatorTestHarness<>(op, new ExecutionConfig(), timerService);

      testHarness.setup();
      testHarness.open();

      timerService.setCurrentTime(0);

      // inject some elements
      final int numElementsFirst = 700;
      final int numElements = 1000;
      for (int i = 0; i < numElementsFirst; i++) {
        testHarness.processElement(new StreamRecord<>(i));
      }

      // draw a snapshot and dispose the window
      System.out.println("GOT: " + testHarness.getOutput());
      int beforeSnapShot = testHarness.getOutput().size();
      StreamStateHandle state = testHarness.snapshot(1L, System.currentTimeMillis());
      List<Integer> resultAtSnapshot = extractFromStreamRecords(testHarness.getOutput());
      int afterSnapShot = testHarness.getOutput().size();
      assertEquals("operator performed computation during snapshot", beforeSnapShot, afterSnapShot);
      assertTrue(afterSnapShot <= numElementsFirst);

      // inject some random elements, which should not show up in the state
      for (int i = 0; i < 300; i++) {
        testHarness.processElement(new StreamRecord<>(i + numElementsFirst));
      }

      op.dispose();

      // re-create the operator and restore the state
      op =
          new AccumulatingProcessingTimeWindowOperator<>(
              validatingIdentityFunction,
              identitySelector,
              IntSerializer.INSTANCE,
              IntSerializer.INSTANCE,
              windowSize,
              windowSize);

      testHarness =
          new OneInputStreamOperatorTestHarness<>(op, new ExecutionConfig(), timerService);

      testHarness.setup();
      testHarness.restore(state);
      testHarness.open();

      // inject some more elements
      for (int i = numElementsFirst; i < numElements; i++) {
        testHarness.processElement(new StreamRecord<>(i));
      }

      timerService.setCurrentTime(400);

      // get and verify the result
      List<Integer> finalResult = new ArrayList<>();
      finalResult.addAll(resultAtSnapshot);
      List<Integer> finalPartialResult = extractFromStreamRecords(testHarness.getOutput());
      finalResult.addAll(finalPartialResult);
      assertEquals(numElements, finalResult.size());

      Collections.sort(finalResult);
      for (int i = 0; i < numElements; i++) {
        assertEquals(i, finalResult.get(i).intValue());
      }
    } catch (Exception e) {
      e.printStackTrace();
      fail(e.getMessage());
    }
  }