/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.beam.runners.direct; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import org.apache.beam.runners.core.StateNamespaces; import org.apache.beam.runners.core.TimerInternals.TimerData; import org.apache.beam.sdk.state.TimeDomain; import org.apache.beam.sdk.transforms.DoFn; import org.apache.beam.sdk.transforms.windowing.BoundedWindow; import org.apache.beam.sdk.transforms.windowing.GlobalWindow; import org.apache.beam.sdk.util.WindowedValue; import org.hamcrest.Matchers; import org.joda.time.Instant; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link DoFnLifecycleManagerRemovingTransformEvaluator}. */ @RunWith(JUnit4.class) public class DoFnLifecycleManagerRemovingTransformEvaluatorTest { private DoFnLifecycleManager lifecycleManager; @Before public void setup() { lifecycleManager = DoFnLifecycleManager.of(new TestFn()); } @Test public void delegatesToUnderlying() throws Exception { ParDoEvaluator<Object> underlying = mock(ParDoEvaluator.class); DoFn<?, ?> original = lifecycleManager.get(); TransformEvaluator<Object> evaluator = DoFnLifecycleManagerRemovingTransformEvaluator.wrapping(underlying, lifecycleManager); WindowedValue<Object> first = WindowedValue.valueInGlobalWindow(new Object()); WindowedValue<Object> second = WindowedValue.valueInGlobalWindow(new Object()); evaluator.processElement(first); verify(underlying).processElement(first); evaluator.processElement(second); verify(underlying).processElement(second); evaluator.finishBundle(); verify(underlying).finishBundle(); } @Test public void removesOnExceptionInProcessElement() throws Exception { ParDoEvaluator<Object> underlying = mock(ParDoEvaluator.class); doThrow(Exception.class).when(underlying).processElement(any(WindowedValue.class)); DoFn<?, ?> original = lifecycleManager.get(); assertThat(original, not(nullValue())); TransformEvaluator<Object> evaluator = DoFnLifecycleManagerRemovingTransformEvaluator.wrapping(underlying, lifecycleManager); try { evaluator.processElement(WindowedValue.valueInGlobalWindow(new Object())); } catch (Exception e) { assertThat(lifecycleManager.get(), not(Matchers.<DoFn<?, ?>>theInstance(original))); return; } fail("Expected underlying evaluator to throw on method call"); } @Test public void removesOnExceptionInOnTimer() throws Exception { ParDoEvaluator<Object> underlying = mock(ParDoEvaluator.class); doThrow(Exception.class) .when(underlying) .onTimer(any(TimerData.class), any(BoundedWindow.class)); DoFn<?, ?> original = lifecycleManager.get(); assertThat(original, not(nullValue())); DoFnLifecycleManagerRemovingTransformEvaluator<Object> evaluator = DoFnLifecycleManagerRemovingTransformEvaluator.wrapping(underlying, lifecycleManager); try { evaluator.onTimer( TimerData.of("foo", StateNamespaces.global(), new Instant(0), TimeDomain.EVENT_TIME), GlobalWindow.INSTANCE); } catch (Exception e) { assertThat(lifecycleManager.get(), not(Matchers.<DoFn<?, ?>>theInstance(original))); return; } fail("Expected underlying evaluator to throw on method call"); } @Test public void removesOnExceptionInFinishBundle() throws Exception { ParDoEvaluator<Object> underlying = mock(ParDoEvaluator.class); doThrow(Exception.class).when(underlying).finishBundle(); DoFn<?, ?> original = lifecycleManager.get(); // the LifecycleManager is set when the evaluator starts assertThat(original, not(nullValue())); TransformEvaluator<Object> evaluator = DoFnLifecycleManagerRemovingTransformEvaluator.wrapping(underlying, lifecycleManager); try { evaluator.finishBundle(); } catch (Exception e) { assertThat(lifecycleManager.get(), Matchers.not(Matchers.<DoFn<?, ?>>theInstance(original))); return; } fail("Expected underlying evaluator to throw on method call"); } private static class TestFn extends DoFn<Object, Object> { @ProcessElement public void processElement(ProcessContext c) throws Exception {} } }