Java Code Examples for org.apache.flink.streaming.util.TestHarnessUtil#getRawElementsFromOutput()

The following examples show how to use org.apache.flink.streaming.util.TestHarnessUtil#getRawElementsFromOutput() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: SourceStreamTaskTest.java    From Flink-CEPplus with Apache License 2.0 6 votes vote down vote up
/**
 * This test verifies that open() and close() are correctly called by the StreamTask.
 */
@Test
@SuppressWarnings("unchecked")
public void testOpenClose() throws Exception {
	final StreamTaskTestHarness<String> testHarness = new StreamTaskTestHarness<>(
			SourceStreamTask::new, BasicTypeInfo.STRING_TYPE_INFO);

	testHarness.setupOutputForSingletonOperatorChain();

	StreamConfig streamConfig = testHarness.getStreamConfig();
	StreamSource<String, ?> sourceOperator = new StreamSource<>(new OpenCloseTestSource());
	streamConfig.setStreamOperator(sourceOperator);
	streamConfig.setOperatorID(new OperatorID());

	testHarness.invoke();
	testHarness.waitForTaskCompletion();

	Assert.assertTrue("RichFunction methods where not called.", OpenCloseTestSource.closeCalled);

	List<String> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
	Assert.assertEquals(10, resultElements.size());
}
 
Example 2
Source File: SourceStreamTaskTest.java    From flink with Apache License 2.0 6 votes vote down vote up
/**
 * This test verifies that open() and close() are correctly called by the StreamTask.
 */
@Test
@SuppressWarnings("unchecked")
public void testOpenClose() throws Exception {
	final StreamTaskTestHarness<String> testHarness = new StreamTaskTestHarness<>(
			SourceStreamTask::new, BasicTypeInfo.STRING_TYPE_INFO);

	testHarness.setupOutputForSingletonOperatorChain();

	StreamConfig streamConfig = testHarness.getStreamConfig();
	StreamSource<String, ?> sourceOperator = new StreamSource<>(new OpenCloseTestSource());
	streamConfig.setStreamOperator(sourceOperator);
	streamConfig.setOperatorID(new OperatorID());

	testHarness.invoke();
	testHarness.waitForTaskCompletion();

	assertTrue("RichFunction methods where not called.", OpenCloseTestSource.closeCalled);

	List<String> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
	Assert.assertEquals(10, resultElements.size());
}
 
Example 3
Source File: SourceStreamTaskTest.java    From flink with Apache License 2.0 6 votes vote down vote up
/**
 * This test verifies that open() and close() are correctly called by the StreamTask.
 */
@Test
public void testOpenClose() throws Exception {
	final StreamTaskTestHarness<String> testHarness = new StreamTaskTestHarness<>(
			SourceStreamTask::new, BasicTypeInfo.STRING_TYPE_INFO);

	testHarness.setupOutputForSingletonOperatorChain();

	StreamConfig streamConfig = testHarness.getStreamConfig();
	StreamSource<String, ?> sourceOperator = new StreamSource<>(new OpenCloseTestSource());
	streamConfig.setStreamOperator(sourceOperator);
	streamConfig.setOperatorID(new OperatorID());

	testHarness.invoke();
	testHarness.waitForTaskCompletion();

	assertTrue("RichFunction methods where not called.", OpenCloseTestSource.closeCalled);

	List<String> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
	Assert.assertEquals(10, resultElements.size());
}
 
Example 4
Source File: SourceStreamTaskTest.java    From Flink-CEPplus with Apache License 2.0 4 votes vote down vote up
/**
 * This test ensures that the SourceStreamTask properly serializes checkpointing
 * and element emission. This also verifies that there are no concurrent invocations
 * of the checkpoint method on the source operator.
 *
 * <p>The source emits elements and performs checkpoints. We have several checkpointer threads
 * that fire checkpoint requests at the source task.
 *
 * <p>If element emission and checkpointing are not in series the count of elements at the
 * beginning of a checkpoint and at the end of a checkpoint are not the same because the
 * source kept emitting elements while the checkpoint was ongoing.
 */
@Test
@SuppressWarnings("unchecked")
public void testCheckpointing() throws Exception {
	final int numElements = 100;
	final int numCheckpoints = 100;
	final int numCheckpointers = 1;
	final int checkpointInterval = 5; // in ms
	final int sourceCheckpointDelay = 1000; // how many random values we sum up in storeCheckpoint
	final int sourceReadDelay = 1; // in ms

	ExecutorService executor = Executors.newFixedThreadPool(10);
	try {
		final TupleTypeInfo<Tuple2<Long, Integer>> typeInfo = new TupleTypeInfo<>(BasicTypeInfo.LONG_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO);

		final StreamTaskTestHarness<Tuple2<Long, Integer>> testHarness = new StreamTaskTestHarness<>(
				SourceStreamTask::new, typeInfo);
		testHarness.setupOutputForSingletonOperatorChain();

		StreamConfig streamConfig = testHarness.getStreamConfig();
		StreamSource<Tuple2<Long, Integer>, ?> sourceOperator = new StreamSource<>(new MockSource(numElements, sourceCheckpointDelay, sourceReadDelay));
		streamConfig.setStreamOperator(sourceOperator);
		streamConfig.setOperatorID(new OperatorID());

		// prepare the

		Future<Boolean>[] checkpointerResults = new Future[numCheckpointers];

		// invoke this first, so the tasks are actually running when the checkpoints are scheduled
		testHarness.invoke();
		testHarness.waitForTaskRunning();

		final StreamTask<Tuple2<Long, Integer>, ?> sourceTask = testHarness.getTask();

		for (int i = 0; i < numCheckpointers; i++) {
			checkpointerResults[i] = executor.submit(new Checkpointer(numCheckpoints, checkpointInterval, sourceTask));
		}

		testHarness.waitForTaskCompletion();

		// Get the result from the checkpointers, if these threw an exception it
		// will be rethrown here
		for (int i = 0; i < numCheckpointers; i++) {
			if (!checkpointerResults[i].isDone()) {
				checkpointerResults[i].cancel(true);
			}
			if (!checkpointerResults[i].isCancelled()) {
				checkpointerResults[i].get();
			}
		}

		List<Tuple2<Long, Integer>> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
		Assert.assertEquals(numElements, resultElements.size());
	}
	finally {
		executor.shutdown();
	}
}
 
Example 5
Source File: TwoInputStreamTaskTest.java    From Flink-CEPplus with Apache License 2.0 4 votes vote down vote up
/**
 * This test verifies that checkpoint barriers are correctly forwarded.
 */
@Test
@SuppressWarnings("unchecked")
public void testCheckpointBarriers() throws Exception {
	final TwoInputStreamTaskTestHarness<String, Integer, String> testHarness =
			new TwoInputStreamTaskTestHarness<String, Integer, String>(
					TwoInputStreamTask::new,
					2, 2, new int[] {1, 2},
					BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO, BasicTypeInfo.STRING_TYPE_INFO);
	testHarness.setupOutputForSingletonOperatorChain();

	StreamConfig streamConfig = testHarness.getStreamConfig();
	CoStreamMap<String, Integer, String> coMapOperator = new CoStreamMap<String, Integer, String>(new IdentityMap());
	streamConfig.setStreamOperator(coMapOperator);
	streamConfig.setOperatorID(new OperatorID());

	ConcurrentLinkedQueue<Object> expectedOutput = new ConcurrentLinkedQueue<Object>();
	long initialTime = 0L;

	testHarness.invoke();
	testHarness.waitForTaskRunning();

	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 0, 0);

	// This element should be buffered since we received a checkpoint barrier on
	// this input
	testHarness.processElement(new StreamRecord<String>("Hello-0-0", initialTime), 0, 0);

	// This one should go through
	testHarness.processElement(new StreamRecord<String>("Ciao-0-0", initialTime), 0, 1);
	expectedOutput.add(new StreamRecord<String>("Ciao-0-0", initialTime));

	testHarness.waitForInputProcessing();

	// These elements should be forwarded, since we did not yet receive a checkpoint barrier
	// on that input, only add to same input, otherwise we would not know the ordering
	// of the output since the Task might read the inputs in any order
	testHarness.processElement(new StreamRecord<Integer>(11, initialTime), 1, 1);
	testHarness.processElement(new StreamRecord<Integer>(111, initialTime), 1, 1);
	expectedOutput.add(new StreamRecord<String>("11", initialTime));
	expectedOutput.add(new StreamRecord<String>("111", initialTime));

	testHarness.waitForInputProcessing();

	// Wait to allow input to end up in the output.
	// TODO Use count down latches instead as a cleaner solution
	for (int i = 0; i < 20; ++i) {
		if (testHarness.getOutput().size() >= expectedOutput.size()) {
			break;
		} else {
			Thread.sleep(100);
		}
	}

	// we should not yet see the barrier, only the two elements from non-blocked input
	TestHarnessUtil.assertOutputEquals("Output was not correct.",
		expectedOutput,
		testHarness.getOutput());

	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 0, 1);
	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 1, 0);
	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 1, 1);

	testHarness.waitForInputProcessing();
	testHarness.endInput();
	testHarness.waitForTaskCompletion();

	// now we should see the barrier and after that the buffered elements
	expectedOutput.add(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()));
	expectedOutput.add(new StreamRecord<String>("Hello-0-0", initialTime));

	TestHarnessUtil.assertOutputEquals("Output was not correct.",
			expectedOutput,
			testHarness.getOutput());

	List<String> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
	Assert.assertEquals(4, resultElements.size());
}
 
Example 6
Source File: SourceStreamTaskTest.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * This test ensures that the SourceStreamTask properly serializes checkpointing
 * and element emission. This also verifies that there are no concurrent invocations
 * of the checkpoint method on the source operator.
 *
 * <p>The source emits elements and performs checkpoints. We have several checkpointer threads
 * that fire checkpoint requests at the source task.
 *
 * <p>If element emission and checkpointing are not in series the count of elements at the
 * beginning of a checkpoint and at the end of a checkpoint are not the same because the
 * source kept emitting elements while the checkpoint was ongoing.
 */
@Test
@SuppressWarnings("unchecked")
public void testCheckpointing() throws Exception {
	final int numElements = 100;
	final int numCheckpoints = 100;
	final int numCheckpointers = 1;
	final int checkpointInterval = 5; // in ms
	final int sourceCheckpointDelay = 1000; // how many random values we sum up in storeCheckpoint
	final int sourceReadDelay = 1; // in ms

	ExecutorService executor = Executors.newFixedThreadPool(10);
	try {
		final TupleTypeInfo<Tuple2<Long, Integer>> typeInfo = new TupleTypeInfo<>(BasicTypeInfo.LONG_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO);

		final StreamTaskTestHarness<Tuple2<Long, Integer>> testHarness = new StreamTaskTestHarness<>(
				SourceStreamTask::new, typeInfo);
		testHarness.setupOutputForSingletonOperatorChain();

		StreamConfig streamConfig = testHarness.getStreamConfig();
		StreamSource<Tuple2<Long, Integer>, ?> sourceOperator = new StreamSource<>(new MockSource(numElements, sourceCheckpointDelay, sourceReadDelay));
		streamConfig.setStreamOperator(sourceOperator);
		streamConfig.setOperatorID(new OperatorID());

		// prepare the

		Future<Boolean>[] checkpointerResults = new Future[numCheckpointers];

		// invoke this first, so the tasks are actually running when the checkpoints are scheduled
		testHarness.invoke();
		testHarness.waitForTaskRunning();

		final StreamTask<Tuple2<Long, Integer>, ?> sourceTask = testHarness.getTask();

		for (int i = 0; i < numCheckpointers; i++) {
			checkpointerResults[i] = executor.submit(new Checkpointer(numCheckpoints, checkpointInterval, sourceTask));
		}

		testHarness.waitForTaskCompletion();

		// Get the result from the checkpointers, if these threw an exception it
		// will be rethrown here
		for (int i = 0; i < numCheckpointers; i++) {
			if (!checkpointerResults[i].isDone()) {
				checkpointerResults[i].cancel(true);
			}
			if (!checkpointerResults[i].isCancelled()) {
				checkpointerResults[i].get();
			}
		}

		List<Tuple2<Long, Integer>> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
		Assert.assertEquals(numElements, resultElements.size());
	}
	finally {
		executor.shutdown();
	}
}
 
Example 7
Source File: TwoInputStreamTaskTest.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * This test verifies that checkpoint barriers are correctly forwarded.
 */
@Test
@SuppressWarnings("unchecked")
public void testCheckpointBarriers() throws Exception {
	if (isInputSelectable) {
		// In the case of selective reading, checkpoints are not currently supported, and we skip this test
		return;
	}

	final TwoInputStreamTaskTestHarness<String, Integer, String> testHarness =
			new TwoInputStreamTaskTestHarness<String, Integer, String>(
					TwoInputStreamTask::new,
					2, 2, new int[] {1, 2},
					BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO, BasicTypeInfo.STRING_TYPE_INFO);
	testHarness.setupOutputForSingletonOperatorChain();

	StreamConfig streamConfig = testHarness.getStreamConfig();
	CoStreamMap<String, Integer, String> coMapOperator = new CoStreamMap<String, Integer, String>(new IdentityMap());
	streamConfig.setStreamOperator(coMapOperator);
	streamConfig.setOperatorID(new OperatorID());

	ConcurrentLinkedQueue<Object> expectedOutput = new ConcurrentLinkedQueue<Object>();
	long initialTime = 0L;

	testHarness.invoke();
	testHarness.waitForTaskRunning();

	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 0, 0);

	// This element should be buffered since we received a checkpoint barrier on
	// this input
	testHarness.processElement(new StreamRecord<String>("Hello-0-0", initialTime), 0, 0);

	// This one should go through
	testHarness.processElement(new StreamRecord<String>("Ciao-0-0", initialTime), 0, 1);
	expectedOutput.add(new StreamRecord<String>("Ciao-0-0", initialTime));

	testHarness.waitForInputProcessing();

	// These elements should be forwarded, since we did not yet receive a checkpoint barrier
	// on that input, only add to same input, otherwise we would not know the ordering
	// of the output since the Task might read the inputs in any order
	testHarness.processElement(new StreamRecord<Integer>(11, initialTime), 1, 1);
	testHarness.processElement(new StreamRecord<Integer>(111, initialTime), 1, 1);
	expectedOutput.add(new StreamRecord<String>("11", initialTime));
	expectedOutput.add(new StreamRecord<String>("111", initialTime));

	testHarness.waitForInputProcessing();

	// Wait to allow input to end up in the output.
	// TODO Use count down latches instead as a cleaner solution
	for (int i = 0; i < 20; ++i) {
		if (testHarness.getOutput().size() >= expectedOutput.size()) {
			break;
		} else {
			Thread.sleep(100);
		}
	}

	// we should not yet see the barrier, only the two elements from non-blocked input
	TestHarnessUtil.assertOutputEquals("Output was not correct.",
		expectedOutput,
		testHarness.getOutput());

	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 0, 1);
	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 1, 0);
	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 1, 1);

	testHarness.waitForInputProcessing();
	testHarness.endInput();
	testHarness.waitForTaskCompletion();

	// now we should see the barrier and after that the buffered elements
	expectedOutput.add(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()));
	expectedOutput.add(new StreamRecord<String>("Hello-0-0", initialTime));

	TestHarnessUtil.assertOutputEquals("Output was not correct.",
			expectedOutput,
			testHarness.getOutput());

	List<String> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
	Assert.assertEquals(4, resultElements.size());
}
 
Example 8
Source File: SourceStreamTaskTest.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * This test ensures that the SourceStreamTask properly serializes checkpointing
 * and element emission. This also verifies that there are no concurrent invocations
 * of the checkpoint method on the source operator.
 *
 * <p>The source emits elements and performs checkpoints. We have several checkpointer threads
 * that fire checkpoint requests at the source task.
 *
 * <p>If element emission and checkpointing are not in series the count of elements at the
 * beginning of a checkpoint and at the end of a checkpoint are not the same because the
 * source kept emitting elements while the checkpoint was ongoing.
 */
@Test
@SuppressWarnings("unchecked")
public void testCheckpointing() throws Exception {
	final int numElements = 100;
	final int numCheckpoints = 100;
	final int numCheckpointers = 1;
	final int checkpointInterval = 5; // in ms
	final int sourceCheckpointDelay = 1000; // how many random values we sum up in storeCheckpoint
	final int sourceReadDelay = 1; // in ms

	ExecutorService executor = Executors.newFixedThreadPool(10);
	try {
		final TupleTypeInfo<Tuple2<Long, Integer>> typeInfo = new TupleTypeInfo<>(BasicTypeInfo.LONG_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO);

		final StreamTaskTestHarness<Tuple2<Long, Integer>> testHarness = new StreamTaskTestHarness<>(
				SourceStreamTask::new, typeInfo);
		testHarness.setupOutputForSingletonOperatorChain();

		StreamConfig streamConfig = testHarness.getStreamConfig();
		StreamSource<Tuple2<Long, Integer>, ?> sourceOperator = new StreamSource<>(new MockSource(numElements, sourceCheckpointDelay, sourceReadDelay));
		streamConfig.setStreamOperator(sourceOperator);
		streamConfig.setOperatorID(new OperatorID());

		// prepare the

		Future<Boolean>[] checkpointerResults = new Future[numCheckpointers];

		// invoke this first, so the tasks are actually running when the checkpoints are scheduled
		testHarness.invoke();
		testHarness.waitForTaskRunning();

		final StreamTask<Tuple2<Long, Integer>, ?> sourceTask = testHarness.getTask();

		for (int i = 0; i < numCheckpointers; i++) {
			checkpointerResults[i] = executor.submit(new Checkpointer(numCheckpoints, checkpointInterval, sourceTask));
		}

		testHarness.waitForTaskCompletion();

		// Get the result from the checkpointers, if these threw an exception it
		// will be rethrown here
		for (int i = 0; i < numCheckpointers; i++) {
			if (!checkpointerResults[i].isDone()) {
				checkpointerResults[i].cancel(true);
			}
			if (!checkpointerResults[i].isCancelled()) {
				checkpointerResults[i].get();
			}
		}

		List<Tuple2<Long, Integer>> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
		Assert.assertEquals(numElements, resultElements.size());
	}
	finally {
		executor.shutdown();
	}
}
 
Example 9
Source File: TwoInputStreamTaskTest.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * This test verifies that checkpoint barriers are correctly forwarded.
 */
@Test
public void testCheckpointBarriers() throws Exception {

	final TwoInputStreamTaskTestHarness<String, Integer, String> testHarness =
			new TwoInputStreamTaskTestHarness<>(
					TwoInputStreamTask::new,
					2, 2, new int[] {1, 2},
					BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.INT_TYPE_INFO, BasicTypeInfo.STRING_TYPE_INFO);
	testHarness.setupOutputForSingletonOperatorChain();

	StreamConfig streamConfig = testHarness.getStreamConfig();
	CoStreamMap<String, Integer, String> coMapOperator = new CoStreamMap<>(new IdentityMap());
	streamConfig.setStreamOperator(coMapOperator);
	streamConfig.setOperatorID(new OperatorID());

	ConcurrentLinkedQueue<Object> expectedOutput = new ConcurrentLinkedQueue<>();
	long initialTime = 0L;

	testHarness.invoke();
	testHarness.waitForTaskRunning();

	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 0, 0);

	// This one should go through
	testHarness.processElement(new StreamRecord<>("Ciao-0-0", initialTime), 0, 1);
	expectedOutput.add(new StreamRecord<>("Ciao-0-0", initialTime));

	testHarness.waitForInputProcessing();

	// These elements should be forwarded, since we did not yet receive a checkpoint barrier
	// on that input, only add to same input, otherwise we would not know the ordering
	// of the output since the Task might read the inputs in any order
	testHarness.processElement(new StreamRecord<>(11, initialTime), 1, 1);
	testHarness.processElement(new StreamRecord<>(111, initialTime), 1, 1);
	expectedOutput.add(new StreamRecord<>("11", initialTime));
	expectedOutput.add(new StreamRecord<>("111", initialTime));

	testHarness.waitForInputProcessing();

	// Wait to allow input to end up in the output.
	// TODO Use count down latches instead as a cleaner solution
	for (int i = 0; i < 20; ++i) {
		if (testHarness.getOutput().size() >= expectedOutput.size()) {
			break;
		} else {
			Thread.sleep(100);
		}
	}

	// we should not yet see the barrier, only the two elements from non-blocked input
	TestHarnessUtil.assertOutputEquals("Output was not correct.",
		expectedOutput,
		testHarness.getOutput());

	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 0, 1);
	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 1, 0);
	testHarness.processEvent(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()), 1, 1);

	testHarness.waitForInputProcessing();
	testHarness.endInput();
	testHarness.waitForTaskCompletion();

	// now we should see the barrier
	expectedOutput.add(new CheckpointBarrier(0, 0, CheckpointOptions.forCheckpointWithDefaultLocation()));

	TestHarnessUtil.assertOutputEquals("Output was not correct.",
			expectedOutput,
			testHarness.getOutput());

	List<String> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
	Assert.assertEquals(3, resultElements.size());
}
 
Example 10
Source File: MultipleInputStreamTaskTest.java    From flink with Apache License 2.0 4 votes vote down vote up
@Test
public void testWatermark() throws Exception {
	try (StreamTaskMailboxTestHarness<String> testHarness =
			new MultipleInputStreamTaskTestHarnessBuilder<>(MultipleInputStreamTask::new, BasicTypeInfo.STRING_TYPE_INFO)
				.addInput(BasicTypeInfo.STRING_TYPE_INFO, 2)
				.addInput(BasicTypeInfo.INT_TYPE_INFO, 2)
				.addInput(BasicTypeInfo.DOUBLE_TYPE_INFO, 2)
				.setupOutputForSingletonOperatorChain(new MapToStringMultipleInputOperatorFactory())
				.build()) {
		ArrayDeque<Object> expectedOutput = new ArrayDeque<>();

		long initialTime = 0L;

		testHarness.processElement(new Watermark(initialTime), 0, 0);
		testHarness.processElement(new Watermark(initialTime), 0, 1);
		testHarness.processElement(new Watermark(initialTime), 1, 0);
		testHarness.processElement(new Watermark(initialTime), 1, 1);

		testHarness.processElement(new Watermark(initialTime), 2, 0);

		assertThat(testHarness.getOutput(), IsEmptyCollection.empty());

		testHarness.processElement(new Watermark(initialTime), 2, 1);

		// now the watermark should have propagated, Map simply forward Watermarks
		expectedOutput.add(new Watermark(initialTime));
		assertThat(testHarness.getOutput(), contains(expectedOutput.toArray()));

		// contrary to checkpoint barriers these elements are not blocked by watermarks
		testHarness.processElement(new StreamRecord<>("Hello", initialTime), 0, 0);
		testHarness.processElement(new StreamRecord<>(42, initialTime), 1, 1);
		expectedOutput.add(new StreamRecord<>("Hello", initialTime));
		expectedOutput.add(new StreamRecord<>("42", initialTime));

		assertThat(testHarness.getOutput(), contains(expectedOutput.toArray()));

		testHarness.processElement(new Watermark(initialTime + 4), 0, 0);
		testHarness.processElement(new Watermark(initialTime + 3), 0, 1);
		testHarness.processElement(new Watermark(initialTime + 3), 1, 0);
		testHarness.processElement(new Watermark(initialTime + 4), 1, 1);
		testHarness.processElement(new Watermark(initialTime + 3), 2, 0);
		testHarness.processElement(new Watermark(initialTime + 2), 2, 1);

		// check whether we get the minimum of all the watermarks, this must also only occur in
		// the output after the two StreamRecords
		expectedOutput.add(new Watermark(initialTime + 2));
		assertThat(testHarness.getOutput(), contains(expectedOutput.toArray()));

		// advance watermark from one of the inputs, now we should get a new one since the
		// minimum increases
		testHarness.processElement(new Watermark(initialTime + 4), 2, 1);
		expectedOutput.add(new Watermark(initialTime + 3));
		assertThat(testHarness.getOutput(), contains(expectedOutput.toArray()));

		// advance the other two inputs, now we should get a new one since the
		// minimum increases again
		testHarness.processElement(new Watermark(initialTime + 4), 0, 1);
		testHarness.processElement(new Watermark(initialTime + 4), 1, 0);
		testHarness.processElement(new Watermark(initialTime + 4), 2, 0);
		expectedOutput.add(new Watermark(initialTime + 4));
		assertThat(testHarness.getOutput(), contains(expectedOutput.toArray()));

		List<String> resultElements = TestHarnessUtil.getRawElementsFromOutput(testHarness.getOutput());
		assertEquals(2, resultElements.size());
	}
}