Repository: incubator-beam Updated Branches: refs/heads/master 05c6c2749 -> b304d037f
http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/75c8bb8d/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTest.java ---------------------------------------------------------------------- diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTest.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTest.java index 447b993..fc468c9 100644 --- a/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTest.java +++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTest.java @@ -17,10 +17,11 @@ */ package org.apache.beam.sdk.transforms.reflect; +import static org.apache.beam.sdk.transforms.reflect.DoFnSignaturesTestUtils.errors; + import com.google.common.reflect.TypeToken; -import java.lang.reflect.Method; -import java.util.List; import org.apache.beam.sdk.transforms.DoFn; +import org.apache.beam.sdk.transforms.reflect.DoFnSignaturesTestUtils.FakeDoFn; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -33,257 +34,21 @@ public class DoFnSignaturesTest { @Rule public ExpectedException thrown = ExpectedException.none(); - private static class FakeDoFn extends DoFn<Integer, String> {} - - @SuppressWarnings({"unused"}) - private void missingProcessContext() {} - - @Test - public void testMissingProcessContext() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - getClass().getName() - + "#missingProcessContext() must take a ProcessContext<> as its first argument"); - - DoFnSignatures.analyzeProcessElementMethod( - TypeToken.of(FakeDoFn.class), - getClass().getDeclaredMethod("missingProcessContext"), - TypeToken.of(Integer.class), - TypeToken.of(String.class)); - } - - @SuppressWarnings({"unused"}) - private void badProcessContext(String s) {} - - @Test - public void testBadProcessContextType() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - getClass().getName() - + "#badProcessContext(String) must take a ProcessContext<> as its first argument"); - - DoFnSignatures.analyzeProcessElementMethod( - TypeToken.of(FakeDoFn.class), - getClass().getDeclaredMethod("badProcessContext", String.class), - TypeToken.of(Integer.class), - TypeToken.of(String.class)); - } - - @SuppressWarnings({"unused"}) - private void badExtraContext(DoFn<Integer, String>.Context c, int n) {} - @Test public void testBadExtraContext() throws Exception { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - getClass().getName() - + "#badExtraContext(Context, int) must have a single argument of type Context"); + thrown.expectMessage("Must take a single argument of type Context"); DoFnSignatures.analyzeBundleMethod( + errors(), TypeToken.of(FakeDoFn.class), - getClass().getDeclaredMethod("badExtraContext", DoFn.Context.class, int.class), + new DoFnSignaturesTestUtils.AnonymousMethod() { + void method(DoFn<Integer, String>.Context c, int n) {} + }.getMethod(), TypeToken.of(Integer.class), TypeToken.of(String.class)); } - @SuppressWarnings({"unused"}) - private void badExtraProcessContext(DoFn<Integer, String>.ProcessContext c, Integer n) {} - - @Test - public void testBadExtraProcessContextType() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - "Integer is not a valid context parameter for method " - + getClass().getName() - + "#badExtraProcessContext(ProcessContext, Integer)" - + ". Should be one of [BoundedWindow]"); - - DoFnSignatures.analyzeProcessElementMethod( - TypeToken.of(FakeDoFn.class), - getClass() - .getDeclaredMethod("badExtraProcessContext", DoFn.ProcessContext.class, Integer.class), - TypeToken.of(Integer.class), - TypeToken.of(String.class)); - } - - @SuppressWarnings("unused") - private int badReturnType() { - return 0; - } - - @Test - public void testBadReturnType() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage(getClass().getName() + "#badReturnType() must have a void return type"); - - DoFnSignatures.analyzeProcessElementMethod( - TypeToken.of(FakeDoFn.class), - getClass().getDeclaredMethod("badReturnType"), - TypeToken.of(Integer.class), - TypeToken.of(String.class)); - } - - @SuppressWarnings("unused") - private void goodConcreteTypes( - DoFn<Integer, String>.ProcessContext c, - DoFn.InputProvider<Integer> input, - DoFn.OutputReceiver<String> output) {} - - @Test - public void testGoodConcreteTypes() throws Exception { - Method method = - getClass() - .getDeclaredMethod( - "goodConcreteTypes", - DoFn.ProcessContext.class, - DoFn.InputProvider.class, - DoFn.OutputReceiver.class); - DoFnSignatures.analyzeProcessElementMethod( - TypeToken.of(FakeDoFn.class), - method, - TypeToken.of(Integer.class), - TypeToken.of(String.class)); - } - - private static class GoodTypeVariables<InputT, OutputT> extends DoFn<InputT, OutputT> { - @ProcessElement - @SuppressWarnings("unused") - public void goodTypeVariables( - DoFn<InputT, OutputT>.ProcessContext c, - DoFn.InputProvider<InputT> input, - DoFn.OutputReceiver<OutputT> output) {} - } - - @Test - public void testGoodTypeVariables() throws Exception { - DoFnSignatures.INSTANCE.getOrParseSignature(GoodTypeVariables.class); - } - - private static class IdentityFn<T> extends DoFn<T, T> { - @ProcessElement - @SuppressWarnings("unused") - public void processElement(ProcessContext c, InputProvider<T> input, OutputReceiver<T> output) { - c.output(c.element()); - } - } - - private static class IdentityListFn<T> extends IdentityFn<List<T>> {} - - @Test - public void testIdentityFnApplied() throws Exception { - DoFnSignatures.INSTANCE.getOrParseSignature(new IdentityFn<String>() {}.getClass()); - } - - @SuppressWarnings("unused") - private void badGenericTwoArgs( - DoFn<Integer, String>.ProcessContext c, - DoFn.InputProvider<Integer> input, - DoFn.OutputReceiver<Integer> output) {} - - @Test - public void testBadGenericsTwoArgs() throws Exception { - Method method = - getClass() - .getDeclaredMethod( - "badGenericTwoArgs", - DoFn.ProcessContext.class, - DoFn.InputProvider.class, - DoFn.OutputReceiver.class); - - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - "Wrong type of OutputReceiver parameter " - + "for method " - + getClass().getName() - + "#badGenericTwoArgs(ProcessContext, InputProvider, OutputReceiver): " - + "OutputReceiver<Integer>, should be " - + "OutputReceiver<String>"); - - DoFnSignatures.analyzeProcessElementMethod( - TypeToken.of(FakeDoFn.class), - method, - TypeToken.of(Integer.class), - TypeToken.of(String.class)); - } - - @SuppressWarnings("unused") - private void badGenericWildCards( - DoFn<Integer, String>.ProcessContext c, - DoFn.InputProvider<Integer> input, - DoFn.OutputReceiver<? super Integer> output) {} - - @Test - public void testBadGenericWildCards() throws Exception { - Method method = - getClass() - .getDeclaredMethod( - "badGenericWildCards", - DoFn.ProcessContext.class, - DoFn.InputProvider.class, - DoFn.OutputReceiver.class); - - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - "Wrong type of OutputReceiver parameter for method " - + getClass().getName() - + "#badGenericWildCards(ProcessContext, InputProvider, OutputReceiver): " - + "OutputReceiver<? super Integer>, should be " - + "OutputReceiver<String>"); - - DoFnSignatures.analyzeProcessElementMethod( - TypeToken.of(FakeDoFn.class), - method, - TypeToken.of(Integer.class), - TypeToken.of(String.class)); - } - - static class BadTypeVariables<InputT, OutputT> extends DoFn<InputT, OutputT> { - @ProcessElement - @SuppressWarnings("unused") - public void badTypeVariables( - DoFn<InputT, OutputT>.ProcessContext c, - DoFn.InputProvider<InputT> input, - DoFn.OutputReceiver<InputT> output) {} - } - - @Test - public void testBadTypeVariables() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - "Wrong type of OutputReceiver parameter for method " - + BadTypeVariables.class.getName() - + "#badTypeVariables(ProcessContext, InputProvider, OutputReceiver): " - + "OutputReceiver<InputT>, should be " - + "OutputReceiver<OutputT>"); - - DoFnSignatures.INSTANCE.getOrParseSignature(BadTypeVariables.class); - } - - @Test - public void testNoProcessElement() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("No method annotated with @ProcessElement found"); - thrown.expectMessage(getClass().getName() + "$"); - DoFnSignatures.INSTANCE.getOrParseSignature(new DoFn<String, String>() {}.getClass()); - } - - @Test - public void testMultipleProcessElement() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Found multiple methods annotated with @ProcessElement"); - thrown.expectMessage("foo()"); - thrown.expectMessage("bar()"); - thrown.expectMessage(getClass().getName() + "$"); - DoFnSignatures.INSTANCE.getOrParseSignature( - new DoFn<String, String>() { - @ProcessElement - public void foo() {} - - @ProcessElement - public void bar() {} - }.getClass()); - } - @Test public void testMultipleStartBundleElement() throws Exception { thrown.expect(IllegalArgumentException.class); @@ -325,21 +90,10 @@ public class DoFnSignaturesTest { } @Test - public void testPrivateProcessElement() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("process() must be public"); - thrown.expectMessage(getClass().getName() + "$"); - DoFnSignatures.INSTANCE.getOrParseSignature( - new DoFn<String, String>() { - @ProcessElement - private void process() {} - }.getClass()); - } - - @Test public void testPrivateStartBundle() throws Exception { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("startBundle() must be public"); + thrown.expectMessage("startBundle()"); + thrown.expectMessage("Must be public"); thrown.expectMessage(getClass().getName() + "$"); DoFnSignatures.INSTANCE.getOrParseSignature( new DoFn<String, String>() { @@ -354,7 +108,8 @@ public class DoFnSignaturesTest { @Test public void testPrivateFinishBundle() throws Exception { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("finishBundle() must be public"); + thrown.expectMessage("finishBundle()"); + thrown.expectMessage("Must be public"); thrown.expectMessage(getClass().getName() + "$"); DoFnSignatures.INSTANCE.getOrParseSignature( new DoFn<String, String>() { http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/75c8bb8d/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTestUtils.java ---------------------------------------------------------------------- diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTestUtils.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTestUtils.java new file mode 100644 index 0000000..88dc423 --- /dev/null +++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/DoFnSignaturesTestUtils.java @@ -0,0 +1,64 @@ +/* + * 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.sdk.transforms.reflect; + +import com.google.common.reflect.TypeToken; +import java.lang.reflect.Method; +import java.util.NoSuchElementException; +import org.apache.beam.sdk.transforms.DoFn; + +/** Utilities for use in {@link DoFnSignatures} tests. */ +class DoFnSignaturesTestUtils { + /** An empty base {@link DoFn} class. */ + static class FakeDoFn extends DoFn<Integer, String> {} + + /** An error reporter. */ + static DoFnSignatures.ErrorReporter errors() { + return new DoFnSignatures.ErrorReporter(null, "[test]"); + } + + /** + * A class for testing utilities that take {@link Method} objects. Use like this: + * + * <pre>{@code + * Method m = new AnonymousMethod() { + * SomeReturnValue method(SomeParameters...) { ... } + * }.getMethod(); // Will return the Method for "method". + * }</pre> + */ + static class AnonymousMethod { + final Method getMethod() throws Exception { + for (Method method : getClass().getDeclaredMethods()) { + if (method.getName().equals("method")) { + return method; + } + } + throw new NoSuchElementException("No method named 'method' defined on " + getClass()); + } + } + + static DoFnSignature.ProcessElementMethod analyzeProcessElementMethod(AnonymousMethod method) + throws Exception { + return DoFnSignatures.analyzeProcessElementMethod( + errors(), + TypeToken.of(FakeDoFn.class), + method.getMethod(), + TypeToken.of(Integer.class), + TypeToken.of(String.class)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/75c8bb8d/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/testhelper/DoFnInvokersTestHelper.java ---------------------------------------------------------------------- diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/testhelper/DoFnInvokersTestHelper.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/testhelper/DoFnInvokersTestHelper.java new file mode 100644 index 0000000..c20a788 --- /dev/null +++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/transforms/reflect/testhelper/DoFnInvokersTestHelper.java @@ -0,0 +1,124 @@ +/* + * 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.sdk.transforms.reflect.testhelper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.verify; + +import org.apache.beam.sdk.transforms.DoFn; +import org.apache.beam.sdk.transforms.reflect.DoFnInvokersTest; + +/** + * Test helper for {@link DoFnInvokersTest}, which needs to test package-private access to DoFns in + * other packages. + */ +public class DoFnInvokersTestHelper { + + private static class StaticPrivateDoFn extends DoFn<String, String> { + @ProcessElement + public void process(ProcessContext c) {} + } + + private class InnerPrivateDoFn extends DoFn<String, String> { + @ProcessElement + public void process(ProcessContext c) {} + } + + static class StaticPackagePrivateDoFn extends DoFn<String, String> { + @ProcessElement + public void process(ProcessContext c) {} + } + + class InnerPackagePrivateDoFn extends DoFn<String, String> { + @ProcessElement + public void process(ProcessContext c) {} + } + + public static DoFn<String, String> newStaticPackagePrivateDoFn() { + return new StaticPackagePrivateDoFn(); + } + + public static void verifyStaticPackagePrivateDoFn( + DoFn<String, String> fn, DoFn<String, String>.ProcessContext context) { + verify((StaticPackagePrivateDoFn) fn).process(context); + } + + public DoFn<String, String> newInnerPackagePrivateDoFn() { + return new InnerPackagePrivateDoFn(); + } + + public static void verifyInnerPackagePrivateDoFn( + DoFn<String, String> fn, DoFn<String, String>.ProcessContext context) { + verify((InnerPackagePrivateDoFn) fn).process(context); + } + + public static DoFn<String, String> newStaticPrivateDoFn() { + return new StaticPrivateDoFn(); + } + + public static void verifyStaticPrivateDoFn( + DoFn<String, String> fn, DoFn<String, String>.ProcessContext context) { + verify((StaticPrivateDoFn) fn).process(context); + } + + public DoFn<String, String> newInnerPrivateDoFn() { + return new InnerPrivateDoFn(); + } + + public static void verifyInnerPrivateDoFn( + DoFn<String, String> fn, DoFn<String, String>.ProcessContext context) { + verify((InnerPrivateDoFn) fn).process(context); + } + + public DoFn<String, String> newInnerAnonymousDoFn() { + return new DoFn<String, String>() { + @ProcessElement + public void process(ProcessContext c) {} + }; + } + + public static void verifyInnerAnonymousDoFn( + DoFn<String, String> fn, DoFn<String, String>.ProcessContext context) throws Exception { + DoFn<String, String> verifier = verify(fn); + verifier.getClass().getMethod("process", DoFn.ProcessContext.class).invoke(verifier, context); + } + + public static DoFn<String, String> newStaticAnonymousDoFn() { + return new DoFn<String, String>() { + private DoFn<String, String>.ProcessContext invokedContext; + + @ProcessElement + public void process(ProcessContext c) { + assertNull("Should have been invoked just once", invokedContext); + invokedContext = c; + } + + @SuppressWarnings("unused") + public void verify(DoFn<String, String>.ProcessContext context) { + assertEquals(context, invokedContext); + } + }; + } + + public static void verifyStaticAnonymousDoFnInvoked( + DoFn<String, String> fn, DoFn<String, String>.ProcessContext context) throws Exception { + + fn.getClass().getMethod("verify", DoFn.ProcessContext.class).invoke(fn, context); + } +}