@Test
  // Test with a single processor failing.
  // One processor fails (to simulate the failure we inject a special message (id > 1000) which causes the processor to
  // throw an exception.
  public void testFailStreamProcessor() {
    final int numBadMessages = 4; // either of these bad messages will cause p1 to throw and exception
    map.put(JobConfig.JOB_DEBOUNCE_TIME_MS, "100");
    map.put("processor.id.to.fail", "101");

    // set number of events we expect to read by both processes in total:
    // p1 will read messageCount/2 messages
    // p2 will read messageCount/2 messages
    // numBadMessages "bad" messages will be generated
    // p2 will read 2 of the "bad" messages
    // p1 will fail on the first of the "bad" messages
    // a new job model will be generated
    // and p2 will read all 2 * messageCount messages again, + numBadMessages (all of them this time)
    // total 2 x messageCount / 2 + numBadMessages/2 + 2 * messageCount + numBadMessages
    int totalEventsToBeConsumed = 3 * messageCount;

    TestStreamTask.endLatch = new CountDownLatch(totalEventsToBeConsumed);
    // create first processor
    CountDownLatch waitStart1 = new CountDownLatch(1);
    CountDownLatch waitStop1 = new CountDownLatch(1);
    StreamProcessor sp1 = createStreamProcessor("101", map, waitStart1, waitStop1);
    // start the first processor
    CountDownLatch stopLatch1 = new CountDownLatch(1);
    Thread t1 = runInThread(sp1, stopLatch1);
    t1.start();

    // start the second processor
    CountDownLatch waitStart2 = new CountDownLatch(1);
    CountDownLatch waitStop2 = new CountDownLatch(1);
    StreamProcessor sp2 = createStreamProcessor("102", map, waitStart2, waitStop2);

    CountDownLatch stopLatch2 = new CountDownLatch(1);
    Thread t2 = runInThread(sp2, stopLatch2);
    t2.start();

    // wait until the 1st processor reports that it has started
    waitForProcessorToStartStop(waitStart1);

    // wait until the 2nd processor reports that it has started
    waitForProcessorToStartStop(waitStart2);

    // produce first batch of messages starting with 0
    produceMessages(0, inputTopic, messageCount);

    // make sure they consume all the messages
    waitUntilMessagesLeftN(totalEventsToBeConsumed - messageCount);
    CountDownLatch containerStopped1 = sp1.containerShutdownLatch;
    CountDownLatch containerStopped2 = sp2.containerShutdownLatch;

    // produce the bad messages
    produceMessages(BAD_MESSAGE_KEY, inputTopic, 4);

    waitForProcessorToStartStop(
        containerStopped1); // TODO: after container failure propagates to StreamProcessor change back

    // wait until the 2nd processor reports that it has stopped its container
    waitForProcessorToStartStop(containerStopped2);

    // give some extra time to let the system to publish and distribute the new job model
    TestZkUtils.sleepMs(300);

    // produce the second batch of the messages, starting with 'messageCount'
    produceMessages(messageCount, inputTopic, messageCount);

    // wait until p2 consumes all the message by itself
    waitUntilMessagesLeftN(0);

    // shutdown p2
    try {
      stopProcessor(stopLatch2);
      t2.join(1000);
    } catch (InterruptedException e) {
      Assert.fail("Failed to join finished thread:" + e.getLocalizedMessage());
    }

    // number of unique values we gonna read is from 0 to (2*messageCount - 1)
    Map<Integer, Boolean> expectedValues = new HashMap<>(2 * messageCount);
    for (int i = 0; i < 2 * messageCount; i++) {
      expectedValues.put(i, false);
    }
    for (int i = BAD_MESSAGE_KEY; i < numBadMessages + BAD_MESSAGE_KEY; i++) {
      //expectedValues.put(i, false);
    }

    verifyNumMessages(outputTopic, expectedValues, totalEventsToBeConsumed);
  }
Failure tests:
ZK unavailable.
One processor fails in process.
samza-test/src/test/java/org/apache/samza/processor/TestZkStreamProcessorFailures.java