| /* |
| * 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.options; |
| |
| import static java.util.Locale.ROOT; |
| import static org.apache.beam.vendor.guava.v20_0.com.google.common.collect.Maps.uniqueIndex; |
| import static org.hamcrest.Matchers.containsString; |
| import static org.hamcrest.Matchers.equalTo; |
| import static org.hamcrest.Matchers.hasItem; |
| import static org.hamcrest.Matchers.isEmptyString; |
| import static org.hamcrest.Matchers.not; |
| import static org.hamcrest.Matchers.notNullValue; |
| import static org.hamcrest.Matchers.nullValue; |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.fasterxml.jackson.annotation.JsonIgnore; |
| import com.fasterxml.jackson.annotation.JsonProperty; |
| import com.fasterxml.jackson.core.JsonGenerator; |
| import com.fasterxml.jackson.core.JsonParser; |
| import com.fasterxml.jackson.core.JsonProcessingException; |
| import com.fasterxml.jackson.databind.DeserializationContext; |
| import com.fasterxml.jackson.databind.JsonDeserializer; |
| import com.fasterxml.jackson.databind.JsonSerializer; |
| import com.fasterxml.jackson.databind.Module; |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| import com.fasterxml.jackson.databind.SerializerProvider; |
| import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| import com.fasterxml.jackson.databind.module.SimpleModule; |
| import com.google.auto.service.AutoService; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import org.apache.beam.model.jobmanagement.v1.JobApi.PipelineOptionDescriptor; |
| import org.apache.beam.model.jobmanagement.v1.JobApi.PipelineOptionType; |
| import org.apache.beam.sdk.Pipeline; |
| import org.apache.beam.sdk.PipelineResult; |
| import org.apache.beam.sdk.PipelineRunner; |
| import org.apache.beam.sdk.runners.PipelineRunnerRegistrar; |
| import org.apache.beam.sdk.testing.CrashingRunner; |
| import org.apache.beam.sdk.testing.ExpectedLogs; |
| import org.apache.beam.sdk.testing.InterceptingUrlClassLoader; |
| import org.apache.beam.sdk.testing.RestoreSystemProperties; |
| import org.apache.beam.sdk.util.common.ReflectHelpers; |
| import org.apache.beam.vendor.guava.v20_0.com.google.common.base.Charsets; |
| import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.ArrayListMultimap; |
| import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.Collections2; |
| import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.ImmutableList; |
| import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.ImmutableMap; |
| import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.ListMultimap; |
| import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.Sets; |
| import org.hamcrest.Matchers; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.ExpectedException; |
| import org.junit.rules.TestRule; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests for {@link PipelineOptionsFactory}. */ |
| @RunWith(JUnit4.class) |
| public class PipelineOptionsFactoryTest { |
| private static final String DEFAULT_RUNNER_NAME = "DirectRunner"; |
| private static final Class<? extends PipelineRunner<?>> REGISTERED_RUNNER = |
| RegisteredTestRunner.class; |
| |
| @Rule public ExpectedException expectedException = ExpectedException.none(); |
| @Rule public TestRule restoreSystemProperties = new RestoreSystemProperties(); |
| @Rule public ExpectedLogs expectedLogs = ExpectedLogs.none(PipelineOptionsFactory.class); |
| |
| @Test |
| public void testAutomaticRegistrationOfPipelineOptions() { |
| assertTrue(PipelineOptionsFactory.getRegisteredOptions().contains(RegisteredTestOptions.class)); |
| } |
| |
| @Test |
| public void testAutomaticRegistrationOfRunners() { |
| assertEquals( |
| REGISTERED_RUNNER, |
| PipelineOptionsFactory.getRegisteredRunners() |
| .get(REGISTERED_RUNNER.getSimpleName().toLowerCase())); |
| } |
| |
| @Test |
| public void testAutomaticRegistrationInculdesWithoutRunnerSuffix() { |
| // Sanity check to make sure the substring works appropriately |
| assertEquals( |
| "RegisteredTest", |
| REGISTERED_RUNNER |
| .getSimpleName() |
| .substring(0, REGISTERED_RUNNER.getSimpleName().length() - "Runner".length())); |
| Map<String, Class<? extends PipelineRunner<?>>> registered = |
| PipelineOptionsFactory.CACHE.get().getSupportedPipelineRunners(); |
| assertEquals( |
| REGISTERED_RUNNER, |
| registered.get( |
| REGISTERED_RUNNER |
| .getSimpleName() |
| .toLowerCase() |
| .substring(0, REGISTERED_RUNNER.getSimpleName().length() - "Runner".length()))); |
| } |
| |
| @Test |
| public void testAppNameIsSet() { |
| ApplicationNameOptions options = PipelineOptionsFactory.as(ApplicationNameOptions.class); |
| assertEquals(PipelineOptionsFactoryTest.class.getSimpleName(), options.getAppName()); |
| } |
| |
| /** A simple test interface. */ |
| public interface TestPipelineOptions extends PipelineOptions { |
| String getTestPipelineOption(); |
| |
| void setTestPipelineOption(String value); |
| } |
| |
| @Test |
| public void testAppNameIsSetWhenUsingAs() { |
| TestPipelineOptions options = PipelineOptionsFactory.as(TestPipelineOptions.class); |
| assertEquals( |
| PipelineOptionsFactoryTest.class.getSimpleName(), |
| options.as(ApplicationNameOptions.class).getAppName()); |
| } |
| |
| @Test |
| public void testOptionsIdIsSet() throws Exception { |
| ObjectMapper mapper = |
| new ObjectMapper() |
| .registerModules(ObjectMapper.findModules(ReflectHelpers.findClassLoader())); |
| PipelineOptions options = PipelineOptionsFactory.create(); |
| // We purposely serialize/deserialize to get another instance. This allows to test if the |
| // default has been set or not. |
| PipelineOptions clone = |
| mapper.readValue(mapper.writeValueAsString(options), PipelineOptions.class); |
| // It is important that we don't call getOptionsId() before we have created the clone. |
| assertEquals(options.getOptionsId(), clone.getOptionsId()); |
| } |
| |
| @Test |
| public void testManualRegistration() { |
| assertFalse(PipelineOptionsFactory.getRegisteredOptions().contains(TestPipelineOptions.class)); |
| PipelineOptionsFactory.register(TestPipelineOptions.class); |
| assertTrue(PipelineOptionsFactory.getRegisteredOptions().contains(TestPipelineOptions.class)); |
| } |
| |
| @Test |
| public void testDefaultRegistration() { |
| assertTrue(PipelineOptionsFactory.getRegisteredOptions().contains(PipelineOptions.class)); |
| } |
| |
| /** A test interface missing a getter. */ |
| public interface MissingGetter extends PipelineOptions { |
| void setObject(Object value); |
| } |
| |
| @Test |
| public void testMissingGetterThrows() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Expected getter for property [object] of type [java.lang.Object] on " |
| + "[org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MissingGetter]."); |
| |
| PipelineOptionsFactory.as(MissingGetter.class); |
| } |
| |
| /** A test interface missing multiple getters. */ |
| public interface MissingMultipleGetters extends MissingGetter { |
| void setOtherObject(Object value); |
| } |
| |
| @Test |
| public void testMultipleMissingGettersThrows() { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "missing property methods on [org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MissingMultipleGetters]"); |
| expectedException.expectMessage("getter for property [object] of type [java.lang.Object]"); |
| expectedException.expectMessage("getter for property [otherObject] of type [java.lang.Object]"); |
| |
| PipelineOptionsFactory.as(MissingMultipleGetters.class); |
| } |
| |
| /** A test interface missing a setter. */ |
| public interface MissingSetter extends PipelineOptions { |
| Object getObject(); |
| } |
| |
| @Test |
| public void testMissingSetterThrows() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Expected setter for property [object] of type [java.lang.Object] on " |
| + "[org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MissingSetter]."); |
| |
| PipelineOptionsFactory.as(MissingSetter.class); |
| } |
| |
| /** A test interface missing multiple setters. */ |
| public interface MissingMultipleSetters extends MissingSetter { |
| Object getOtherObject(); |
| } |
| |
| @Test |
| public void testMissingMultipleSettersThrows() { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "missing property methods on [org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MissingMultipleSetters]"); |
| expectedException.expectMessage("setter for property [object] of type [java.lang.Object]"); |
| expectedException.expectMessage("setter for property [otherObject] of type [java.lang.Object]"); |
| |
| PipelineOptionsFactory.as(MissingMultipleSetters.class); |
| } |
| |
| /** A test interface missing a setter and a getter. */ |
| public interface MissingGettersAndSetters extends MissingGetter { |
| Object getOtherObject(); |
| } |
| |
| @Test |
| public void testMissingGettersAndSettersThrows() { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "missing property methods on [org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MissingGettersAndSetters]"); |
| expectedException.expectMessage("getter for property [object] of type [java.lang.Object]"); |
| expectedException.expectMessage("setter for property [otherObject] of type [java.lang.Object]"); |
| |
| PipelineOptionsFactory.as(MissingGettersAndSetters.class); |
| } |
| |
| /** A test interface with a type mismatch between the getter and setter. */ |
| public interface GetterSetterTypeMismatch extends PipelineOptions { |
| boolean getValue(); |
| |
| void setValue(int value); |
| } |
| |
| @Test |
| public void testGetterSetterTypeMismatchThrows() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Type mismatch between getter and setter methods for property [value]. Getter is of type " |
| + "[boolean] whereas setter is of type [int]."); |
| |
| PipelineOptionsFactory.as(GetterSetterTypeMismatch.class); |
| } |
| |
| /** A test interface with multiple type mismatches between getters and setters. */ |
| public interface MultiGetterSetterTypeMismatch extends GetterSetterTypeMismatch { |
| long getOther(); |
| |
| void setOther(String other); |
| } |
| |
| @Test |
| public void testMultiGetterSetterTypeMismatchThrows() { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("Type mismatches between getters and setters detected:"); |
| expectedException.expectMessage( |
| "Property [value]: Getter is of type " + "[boolean] whereas setter is of type [int]."); |
| expectedException.expectMessage( |
| "Property [other]: Getter is of type [long] " |
| + "whereas setter is of type [class java.lang.String]."); |
| PipelineOptionsFactory.as(MultiGetterSetterTypeMismatch.class); |
| } |
| |
| /** A test interface representing a composite interface. */ |
| public interface CombinedObject extends MissingGetter, MissingSetter {} |
| |
| @Test |
| public void testHavingSettersGettersFromSeparateInterfacesIsValid() { |
| PipelineOptionsFactory.as(CombinedObject.class); |
| } |
| |
| /** A test interface that contains a non-bean style method. */ |
| public interface ExtraneousMethod extends PipelineOptions { |
| String extraneousMethod(int value, String otherValue); |
| } |
| |
| @Test |
| public void testHavingExtraneousMethodThrows() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Methods [extraneousMethod(int, String)] on " |
| + "[org.apache.beam.sdk.options.PipelineOptionsFactoryTest$ExtraneousMethod] " |
| + "do not conform to being bean properties."); |
| |
| PipelineOptionsFactory.as(ExtraneousMethod.class); |
| } |
| |
| /** A test interface that has a conflicting return type with its parent. */ |
| public interface ReturnTypeConflict extends CombinedObject { |
| @Override |
| String getObject(); |
| |
| void setObject(String value); |
| } |
| |
| @Test |
| public void testReturnTypeConflictThrows() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Method [getObject] has multiple definitions [public abstract java.lang.Object " |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MissingSetter" |
| + ".getObject(), public abstract java.lang.String " |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$ReturnTypeConflict" |
| + ".getObject()] with different return types for [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$ReturnTypeConflict]."); |
| PipelineOptionsFactory.as(ReturnTypeConflict.class); |
| } |
| |
| /** An interface to provide multiple methods with return type conflicts. */ |
| public interface MultiReturnTypeConflictBase extends CombinedObject { |
| Object getOther(); |
| |
| void setOther(Object object); |
| } |
| |
| /** A test interface that has multiple conflicting return types with its parent. */ |
| public interface MultiReturnTypeConflict extends MultiReturnTypeConflictBase { |
| @Override |
| String getObject(); |
| |
| void setObject(String value); |
| |
| @Override |
| Long getOther(); |
| |
| void setOther(Long other); |
| } |
| |
| @Test |
| public void testMultipleReturnTypeConflictsThrows() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "[org.apache.beam.sdk.options." + "PipelineOptionsFactoryTest$MultiReturnTypeConflict]"); |
| expectedException.expectMessage( |
| "Methods with multiple definitions with different return types"); |
| expectedException.expectMessage("Method [getObject] has multiple definitions"); |
| expectedException.expectMessage( |
| "public abstract java.lang.Object " |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$" |
| + "MissingSetter.getObject()"); |
| expectedException.expectMessage( |
| "public abstract java.lang.String org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultiReturnTypeConflict.getObject()"); |
| expectedException.expectMessage("Method [getOther] has multiple definitions"); |
| expectedException.expectMessage( |
| "public abstract java.lang.Object " |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$" |
| + "MultiReturnTypeConflictBase.getOther()"); |
| expectedException.expectMessage( |
| "public abstract java.lang.Long org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultiReturnTypeConflict.getOther()"); |
| |
| PipelineOptionsFactory.as(MultiReturnTypeConflict.class); |
| } |
| |
| /** Test interface that has {@link JsonIgnore @JsonIgnore} on a setter for a property. */ |
| public interface SetterWithJsonIgnore extends PipelineOptions { |
| String getValue(); |
| |
| @JsonIgnore |
| void setValue(String value); |
| } |
| |
| @Test |
| public void testSetterAnnotatedWithJsonIgnore() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Expected setter for property [value] to not be marked with @JsonIgnore on [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$SetterWithJsonIgnore]"); |
| PipelineOptionsFactory.as(SetterWithJsonIgnore.class); |
| } |
| |
| /** Test interface that has {@link JsonIgnore @JsonIgnore} on multiple setters. */ |
| public interface MultiSetterWithJsonIgnore extends SetterWithJsonIgnore { |
| Integer getOther(); |
| |
| @JsonIgnore |
| void setOther(Integer other); |
| } |
| |
| @Test |
| public void testMultipleSettersAnnotatedWithJsonIgnore() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("Found setters marked with @JsonIgnore:"); |
| expectedException.expectMessage( |
| "property [other] should not be marked with @JsonIgnore on [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MultiSetterWithJsonIgnore]"); |
| expectedException.expectMessage( |
| "property [value] should not be marked with @JsonIgnore on [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$SetterWithJsonIgnore]"); |
| PipelineOptionsFactory.as(MultiSetterWithJsonIgnore.class); |
| } |
| |
| /** |
| * This class is has a conflicting field with {@link CombinedObject} that doesn't have {@link |
| * JsonIgnore @JsonIgnore}. |
| */ |
| public interface GetterWithJsonIgnore extends PipelineOptions { |
| @JsonIgnore |
| Object getObject(); |
| |
| void setObject(Object value); |
| } |
| |
| /** |
| * This class is has a conflicting {@link JsonIgnore @JsonIgnore} value with {@link |
| * GetterWithJsonIgnore}. |
| */ |
| public interface GetterWithInconsistentJsonIgnoreValue extends PipelineOptions { |
| @JsonIgnore(value = false) |
| Object getObject(); |
| |
| void setObject(Object value); |
| } |
| |
| @Test |
| public void testNotAllGettersAnnotatedWithJsonIgnore() throws Exception { |
| // Initial construction is valid. |
| GetterWithJsonIgnore options = PipelineOptionsFactory.as(GetterWithJsonIgnore.class); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Expected getter for property [object] to be marked with @JsonIgnore on all [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$GetterWithJsonIgnore, " |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MissingSetter], " |
| + "found only on [org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$GetterWithJsonIgnore]"); |
| |
| // When we attempt to convert, we should error at this moment. |
| options.as(CombinedObject.class); |
| } |
| |
| /** Test interface. */ |
| public interface MultiGetters extends PipelineOptions { |
| Object getObject(); |
| |
| void setObject(Object value); |
| |
| @JsonIgnore |
| Integer getOther(); |
| |
| void setOther(Integer value); |
| |
| Void getConsistent(); |
| |
| void setConsistent(Void consistent); |
| } |
| |
| /** Test interface. */ |
| public interface MultipleGettersWithInconsistentJsonIgnore extends PipelineOptions { |
| @JsonIgnore |
| Object getObject(); |
| |
| void setObject(Object value); |
| |
| Integer getOther(); |
| |
| void setOther(Integer value); |
| |
| Void getConsistent(); |
| |
| void setConsistent(Void consistent); |
| } |
| |
| @Test |
| public void testMultipleGettersWithInconsistentJsonIgnore() { |
| // Initial construction is valid. |
| MultiGetters options = PipelineOptionsFactory.as(MultiGetters.class); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("Property getters are inconsistently marked with @JsonIgnore:"); |
| expectedException.expectMessage("property [object] to be marked on all"); |
| expectedException.expectMessage( |
| "found only on [org.apache.beam.sdk.options." + "PipelineOptionsFactoryTest$MultiGetters]"); |
| expectedException.expectMessage("property [other] to be marked on all"); |
| expectedException.expectMessage( |
| "found only on [org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultipleGettersWithInconsistentJsonIgnore]"); |
| |
| expectedException.expectMessage( |
| Matchers.anyOf( |
| containsString( |
| java.util.Arrays.toString( |
| new String[] { |
| "org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultipleGettersWithInconsistentJsonIgnore", |
| "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MultiGetters" |
| })), |
| containsString( |
| java.util.Arrays.toString( |
| new String[] { |
| "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MultiGetters", |
| "org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultipleGettersWithInconsistentJsonIgnore" |
| })))); |
| expectedException.expectMessage(not(containsString("property [consistent]"))); |
| |
| // When we attempt to convert, we should error immediately |
| options.as(MultipleGettersWithInconsistentJsonIgnore.class); |
| } |
| |
| /** Test interface that has {@link Default @Default} on a setter for a property. */ |
| public interface SetterWithDefault extends PipelineOptions { |
| String getValue(); |
| |
| @Default.String("abc") |
| void setValue(String value); |
| } |
| |
| @Test |
| public void testSetterAnnotatedWithDefault() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Expected setter for property [value] to not be marked with @Default on [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$SetterWithDefault]"); |
| PipelineOptionsFactory.as(SetterWithDefault.class); |
| } |
| |
| /** Test interface that has {@link Default @Default} on multiple setters. */ |
| public interface MultiSetterWithDefault extends SetterWithDefault { |
| Integer getOther(); |
| |
| @Default.String("abc") |
| void setOther(Integer other); |
| } |
| |
| @Test |
| public void testMultipleSettersAnnotatedWithDefault() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("Found setters marked with @Default:"); |
| expectedException.expectMessage( |
| "property [other] should not be marked with @Default on [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MultiSetterWithDefault]"); |
| expectedException.expectMessage( |
| "property [value] should not be marked with @Default on [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$SetterWithDefault]"); |
| PipelineOptionsFactory.as(MultiSetterWithDefault.class); |
| } |
| |
| /** |
| * This class is has a conflicting field with {@link CombinedObject} that doesn't have {@link |
| * Default @Default}. |
| */ |
| public interface GetterWithDefault extends PipelineOptions { |
| @Default.Integer(1) |
| Object getObject(); |
| |
| void setObject(Object value); |
| } |
| |
| /** |
| * This class is consistent with {@link GetterWithDefault} that has the same {@link |
| * Default @Default}. |
| */ |
| public interface GetterWithConsistentDefault extends PipelineOptions { |
| @Default.Integer(1) |
| Object getObject(); |
| |
| void setObject(Object value); |
| } |
| |
| /** |
| * This class is inconsistent with {@link GetterWithDefault} that has a different {@link |
| * Default @Default}. |
| */ |
| public interface GetterWithInconsistentDefaultType extends PipelineOptions { |
| @Default.String("abc") |
| Object getObject(); |
| |
| void setObject(Object value); |
| } |
| |
| /** |
| * This class is inconsistent with {@link GetterWithDefault} that has a different {@link |
| * Default @Default} value. |
| */ |
| public interface GetterWithInconsistentDefaultValue extends PipelineOptions { |
| @Default.Integer(0) |
| Object getObject(); |
| |
| void setObject(Object value); |
| } |
| |
| @Test |
| public void testNotAllGettersAnnotatedWithDefault() throws Exception { |
| // Initial construction is valid. |
| GetterWithDefault options = PipelineOptionsFactory.as(GetterWithDefault.class); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Expected getter for property [object] to be marked with @Default on all [" |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$GetterWithDefault, " |
| + "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MissingSetter], " |
| + "found only on [org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$GetterWithDefault]"); |
| |
| // When we attempt to convert, we should error at this moment. |
| options.as(CombinedObject.class); |
| } |
| |
| @Test |
| public void testGettersAnnotatedWithConsistentDefault() throws Exception { |
| GetterWithConsistentDefault options = |
| PipelineOptionsFactory.as(GetterWithDefault.class).as(GetterWithConsistentDefault.class); |
| |
| assertEquals(1, options.getObject()); |
| } |
| |
| @Test |
| public void testGettersAnnotatedWithInconsistentDefault() throws Exception { |
| // Initial construction is valid. |
| GetterWithDefault options = PipelineOptionsFactory.as(GetterWithDefault.class); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Property [object] is marked with contradictory annotations. Found [" |
| + "[Default.Integer(value=1) on org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$GetterWithDefault#getObject()], " |
| + "[Default.String(value=abc) on org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$GetterWithInconsistentDefaultType#getObject()]]."); |
| |
| // When we attempt to convert, we should error at this moment. |
| options.as(GetterWithInconsistentDefaultType.class); |
| } |
| |
| @Test |
| public void testGettersAnnotatedWithInconsistentDefaultValue() throws Exception { |
| // Initial construction is valid. |
| GetterWithDefault options = PipelineOptionsFactory.as(GetterWithDefault.class); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Property [object] is marked with contradictory annotations. Found [" |
| + "[Default.Integer(value=1) on org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$GetterWithDefault#getObject()], " |
| + "[Default.Integer(value=0) on org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$GetterWithInconsistentDefaultValue#getObject()]]."); |
| |
| // When we attempt to convert, we should error at this moment. |
| options.as(GetterWithInconsistentDefaultValue.class); |
| } |
| |
| @Test |
| public void testGettersAnnotatedWithInconsistentJsonIgnoreValue() throws Exception { |
| // Initial construction is valid. |
| GetterWithJsonIgnore options = PipelineOptionsFactory.as(GetterWithJsonIgnore.class); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Property [object] is marked with contradictory annotations. Found [" |
| + "[JsonIgnore(value=false) on org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$GetterWithInconsistentJsonIgnoreValue#getObject()], " |
| + "[JsonIgnore(value=true) on org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$GetterWithJsonIgnore#getObject()]]."); |
| |
| // When we attempt to convert, we should error at this moment. |
| options.as(GetterWithInconsistentJsonIgnoreValue.class); |
| } |
| |
| /** Test interface. */ |
| public interface GettersWithMultipleDefault extends PipelineOptions { |
| @Default.String("abc") |
| @Default.Integer(0) |
| Object getObject(); |
| |
| void setObject(Object value); |
| } |
| |
| @Test |
| public void testGettersWithMultipleDefaults() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Property [object] is marked with contradictory annotations. Found [" |
| + "[Default.String(value=abc) on org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$GettersWithMultipleDefault#getObject()], " |
| + "[Default.Integer(value=0) on org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$GettersWithMultipleDefault#getObject()]]."); |
| |
| // When we attempt to create, we should error at this moment. |
| PipelineOptionsFactory.as(GettersWithMultipleDefault.class); |
| } |
| |
| /** Test interface. */ |
| public interface MultiGettersWithDefault extends PipelineOptions { |
| Object getObject(); |
| |
| void setObject(Object value); |
| |
| @Default.Integer(1) |
| Integer getOther(); |
| |
| void setOther(Integer value); |
| |
| Void getConsistent(); |
| |
| void setConsistent(Void consistent); |
| } |
| |
| /** Test interface. */ |
| public interface MultipleGettersWithInconsistentDefault extends PipelineOptions { |
| @Default.Boolean(true) |
| Object getObject(); |
| |
| void setObject(Object value); |
| |
| Integer getOther(); |
| |
| void setOther(Integer value); |
| |
| Void getConsistent(); |
| |
| void setConsistent(Void consistent); |
| } |
| |
| @Test |
| public void testMultipleGettersWithInconsistentDefault() { |
| // Initial construction is valid. |
| MultiGettersWithDefault options = PipelineOptionsFactory.as(MultiGettersWithDefault.class); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("Property getters are inconsistently marked with @Default:"); |
| expectedException.expectMessage("property [object] to be marked on all"); |
| expectedException.expectMessage( |
| "found only on [org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultiGettersWithDefault]"); |
| expectedException.expectMessage("property [other] to be marked on all"); |
| expectedException.expectMessage( |
| "found only on [org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultipleGettersWithInconsistentDefault]"); |
| |
| expectedException.expectMessage( |
| Matchers.anyOf( |
| containsString( |
| java.util.Arrays.toString( |
| new String[] { |
| "org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultipleGettersWithInconsistentDefault", |
| "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MultiGettersWithDefault" |
| })), |
| containsString( |
| java.util.Arrays.toString( |
| new String[] { |
| "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$MultiGettersWithDefault", |
| "org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$MultipleGettersWithInconsistentDefault" |
| })))); |
| expectedException.expectMessage(not(containsString("property [consistent]"))); |
| |
| // When we attempt to convert, we should error immediately |
| options.as(MultipleGettersWithInconsistentDefault.class); |
| } |
| |
| @Test |
| public void testAppNameIsNotOverriddenWhenPassedInViaCommandLine() { |
| ApplicationNameOptions options = |
| PipelineOptionsFactory.fromArgs("--appName=testAppName").as(ApplicationNameOptions.class); |
| assertEquals("testAppName", options.getAppName()); |
| } |
| |
| @Test |
| public void testPropertyIsSetOnRegisteredPipelineOptionNotPartOfOriginalInterface() { |
| PipelineOptions options = PipelineOptionsFactory.fromArgs("--streaming").create(); |
| assertTrue(options.as(StreamingOptions.class).isStreaming()); |
| } |
| |
| /** A test interface containing all the primitives. */ |
| public interface Primitives extends PipelineOptions { |
| boolean getBoolean(); |
| |
| void setBoolean(boolean value); |
| |
| char getChar(); |
| |
| void setChar(char value); |
| |
| byte getByte(); |
| |
| void setByte(byte value); |
| |
| short getShort(); |
| |
| void setShort(short value); |
| |
| int getInt(); |
| |
| void setInt(int value); |
| |
| long getLong(); |
| |
| void setLong(long value); |
| |
| float getFloat(); |
| |
| void setFloat(float value); |
| |
| double getDouble(); |
| |
| void setDouble(double value); |
| } |
| |
| @Test |
| public void testPrimitives() { |
| String[] args = |
| new String[] { |
| "--boolean=true", |
| "--char=d", |
| "--byte=12", |
| "--short=300", |
| "--int=100000", |
| "--long=123890123890", |
| "--float=55.5", |
| "--double=12.3" |
| }; |
| |
| Primitives options = PipelineOptionsFactory.fromArgs(args).as(Primitives.class); |
| assertTrue(options.getBoolean()); |
| assertEquals('d', options.getChar()); |
| assertEquals((byte) 12, options.getByte()); |
| assertEquals((short) 300, options.getShort()); |
| assertEquals(100000, options.getInt()); |
| assertEquals(123890123890L, options.getLong()); |
| assertEquals(55.5f, options.getFloat(), 0.0f); |
| assertEquals(12.3, options.getDouble(), 0.0); |
| } |
| |
| @Test |
| public void testBooleanShorthandArgument() { |
| String[] args = new String[] {"--boolean"}; |
| |
| Primitives options = PipelineOptionsFactory.fromArgs(args).as(Primitives.class); |
| assertTrue(options.getBoolean()); |
| } |
| |
| @Test |
| public void testEmptyValueNotAllowed() { |
| String[] args = new String[] {"--byte="}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| PipelineOptionsFactory.fromArgs(args).as(Primitives.class); |
| } |
| |
| /** Enum used for testing PipelineOptions CLI parsing. */ |
| public enum TestEnum { |
| Value, |
| Value2 |
| } |
| |
| /** A test interface containing all supported objects. */ |
| public interface Objects extends PipelineOptions { |
| Boolean getBoolean(); |
| |
| void setBoolean(Boolean value); |
| |
| Character getChar(); |
| |
| void setChar(Character value); |
| |
| Byte getByte(); |
| |
| void setByte(Byte value); |
| |
| Short getShort(); |
| |
| void setShort(Short value); |
| |
| Integer getInt(); |
| |
| void setInt(Integer value); |
| |
| Long getLong(); |
| |
| void setLong(Long value); |
| |
| Float getFloat(); |
| |
| void setFloat(Float value); |
| |
| Double getDouble(); |
| |
| void setDouble(Double value); |
| |
| String getString(); |
| |
| void setString(String value); |
| |
| String getEmptyString(); |
| |
| void setEmptyString(String value); |
| |
| Class<?> getClassValue(); |
| |
| void setClassValue(Class<?> value); |
| |
| TestEnum getEnum(); |
| |
| void setEnum(TestEnum value); |
| |
| ValueProvider<String> getStringValue(); |
| |
| void setStringValue(ValueProvider<String> value); |
| |
| ValueProvider<Long> getLongValue(); |
| |
| void setLongValue(ValueProvider<Long> value); |
| |
| ValueProvider<TestEnum> getEnumValue(); |
| |
| void setEnumValue(ValueProvider<TestEnum> value); |
| } |
| |
| @Test |
| public void testObjects() { |
| String[] args = |
| new String[] { |
| "--boolean=true", |
| "--char=d", |
| "--byte=12", |
| "--short=300", |
| "--int=100000", |
| "--long=123890123890", |
| "--float=55.5", |
| "--double=12.3", |
| "--string=stringValue", |
| "--emptyString=", |
| "--classValue=" + PipelineOptionsFactoryTest.class.getName(), |
| "--enum=" + TestEnum.Value, |
| "--stringValue=beam", |
| "--longValue=12389049585840", |
| "--enumValue=" + TestEnum.Value |
| }; |
| |
| Objects options = PipelineOptionsFactory.fromArgs(args).as(Objects.class); |
| assertTrue(options.getBoolean()); |
| assertEquals(Character.valueOf('d'), options.getChar()); |
| assertEquals(Byte.valueOf((byte) 12), options.getByte()); |
| assertEquals(Short.valueOf((short) 300), options.getShort()); |
| assertEquals(Integer.valueOf(100000), options.getInt()); |
| assertEquals(Long.valueOf(123890123890L), options.getLong()); |
| assertEquals(55.5f, options.getFloat(), 0.0f); |
| assertEquals(12.3, options.getDouble(), 0.0); |
| assertEquals("stringValue", options.getString()); |
| assertTrue(options.getEmptyString().isEmpty()); |
| assertEquals(PipelineOptionsFactoryTest.class, options.getClassValue()); |
| assertEquals(TestEnum.Value, options.getEnum()); |
| assertEquals("beam", options.getStringValue().get()); |
| assertEquals(Long.valueOf(12389049585840L), options.getLongValue().get()); |
| assertEquals(TestEnum.Value, options.getEnumValue().get()); |
| } |
| |
| @Test |
| public void testStringValueProvider() { |
| String[] args = new String[] {"--stringValue=beam"}; |
| String[] emptyArgs = new String[] {"--stringValue="}; |
| Objects options = PipelineOptionsFactory.fromArgs(args).as(Objects.class); |
| assertEquals("beam", options.getStringValue().get()); |
| options = PipelineOptionsFactory.fromArgs(emptyArgs).as(Objects.class); |
| assertEquals("", options.getStringValue().get()); |
| } |
| |
| @Test |
| public void testLongValueProvider() { |
| String[] args = new String[] {"--longValue=12345678762"}; |
| String[] emptyArgs = new String[] {"--longValue="}; |
| Objects options = PipelineOptionsFactory.fromArgs(args).as(Objects.class); |
| assertEquals(Long.valueOf(12345678762L), options.getLongValue().get()); |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| PipelineOptionsFactory.fromArgs(emptyArgs).as(Objects.class); |
| } |
| |
| @Test |
| public void testEnumValueProvider() { |
| String[] args = new String[] {"--enumValue=" + TestEnum.Value}; |
| String[] emptyArgs = new String[] {"--enumValue="}; |
| Objects options = PipelineOptionsFactory.fromArgs(args).as(Objects.class); |
| assertEquals(TestEnum.Value, options.getEnumValue().get()); |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| PipelineOptionsFactory.fromArgs(emptyArgs).as(Objects.class); |
| } |
| |
| /** A test class for verifying JSON -> Object conversion. */ |
| public static class ComplexType { |
| String value; |
| String value2; |
| |
| public ComplexType(@JsonProperty("key") String value, @JsonProperty("key2") String value2) { |
| this.value = value; |
| this.value2 = value2; |
| } |
| } |
| |
| /** A test interface for verifying JSON -> complex type conversion. */ |
| public interface ComplexTypes extends PipelineOptions { |
| Map<String, String> getMap(); |
| |
| void setMap(Map<String, String> value); |
| |
| ComplexType getObject(); |
| |
| void setObject(ComplexType value); |
| |
| ValueProvider<ComplexType> getObjectValue(); |
| |
| void setObjectValue(ValueProvider<ComplexType> value); |
| } |
| |
| @Test |
| public void testComplexTypes() { |
| String[] args = |
| new String[] { |
| "--map={\"key\":\"value\",\"key2\":\"value2\"}", |
| "--object={\"key\":\"value\",\"key2\":\"value2\"}", |
| "--objectValue={\"key\":\"value\",\"key2\":\"value2\"}" |
| }; |
| ComplexTypes options = PipelineOptionsFactory.fromArgs(args).as(ComplexTypes.class); |
| assertEquals(ImmutableMap.of("key", "value", "key2", "value2"), options.getMap()); |
| assertEquals("value", options.getObject().value); |
| assertEquals("value2", options.getObject().value2); |
| assertEquals("value", options.getObjectValue().get().value); |
| assertEquals("value2", options.getObjectValue().get().value2); |
| } |
| |
| @Test |
| public void testMissingArgument() { |
| String[] args = new String[] {}; |
| |
| Objects options = PipelineOptionsFactory.fromArgs(args).as(Objects.class); |
| assertNull(options.getString()); |
| } |
| |
| /** A test interface containing all supported array return types. */ |
| public interface Arrays extends PipelineOptions { |
| boolean[] getBoolean(); |
| |
| void setBoolean(boolean[] value); |
| |
| char[] getChar(); |
| |
| void setChar(char[] value); |
| |
| short[] getShort(); |
| |
| void setShort(short[] value); |
| |
| int[] getInt(); |
| |
| void setInt(int[] value); |
| |
| long[] getLong(); |
| |
| void setLong(long[] value); |
| |
| float[] getFloat(); |
| |
| void setFloat(float[] value); |
| |
| double[] getDouble(); |
| |
| void setDouble(double[] value); |
| |
| String[] getString(); |
| |
| void setString(String[] value); |
| |
| Class<?>[] getClassValue(); |
| |
| void setClassValue(Class<?>[] value); |
| |
| TestEnum[] getEnum(); |
| |
| void setEnum(TestEnum[] value); |
| |
| ValueProvider<String[]> getStringValue(); |
| |
| void setStringValue(ValueProvider<String[]> value); |
| |
| ValueProvider<Long[]> getLongValue(); |
| |
| void setLongValue(ValueProvider<Long[]> value); |
| |
| ValueProvider<TestEnum[]> getEnumValue(); |
| |
| void setEnumValue(ValueProvider<TestEnum[]> value); |
| } |
| |
| @Test |
| @SuppressWarnings("rawtypes") |
| public void testArrays() { |
| String[] args = |
| new String[] { |
| "--boolean=true", |
| "--boolean=true", |
| "--boolean=false", |
| "--char=d", |
| "--char=e", |
| "--char=f", |
| "--short=300", |
| "--short=301", |
| "--short=302", |
| "--int=100000", |
| "--int=100001", |
| "--int=100002", |
| "--long=123890123890", |
| "--long=123890123891", |
| "--long=123890123892", |
| "--float=55.5", |
| "--float=55.6", |
| "--float=55.7", |
| "--double=12.3", |
| "--double=12.4", |
| "--double=12.5", |
| "--string=stringValue1", |
| "--string=stringValue2", |
| "--string=stringValue3", |
| "--classValue=" + PipelineOptionsFactory.class.getName(), |
| "--classValue=" + PipelineOptionsFactoryTest.class.getName(), |
| "--enum=" + TestEnum.Value, |
| "--enum=" + TestEnum.Value2, |
| "--stringValue=abc", |
| "--stringValue=beam", |
| "--longValue=123890123890", |
| "--longValue=123890123891", |
| "--enumValue=" + TestEnum.Value, |
| "--enumValue=" + TestEnum.Value2 |
| }; |
| |
| Arrays options = PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| boolean[] bools = options.getBoolean(); |
| assertTrue(bools[0] && bools[1] && !bools[2]); |
| assertArrayEquals(new char[] {'d', 'e', 'f'}, options.getChar()); |
| assertArrayEquals(new short[] {300, 301, 302}, options.getShort()); |
| assertArrayEquals(new int[] {100000, 100001, 100002}, options.getInt()); |
| assertArrayEquals(new long[] {123890123890L, 123890123891L, 123890123892L}, options.getLong()); |
| assertArrayEquals(new float[] {55.5f, 55.6f, 55.7f}, options.getFloat(), 0.0f); |
| assertArrayEquals(new double[] {12.3, 12.4, 12.5}, options.getDouble(), 0.0); |
| assertArrayEquals( |
| new String[] {"stringValue1", "stringValue2", "stringValue3"}, options.getString()); |
| assertArrayEquals( |
| new Class[] {PipelineOptionsFactory.class, PipelineOptionsFactoryTest.class}, |
| options.getClassValue()); |
| assertArrayEquals(new TestEnum[] {TestEnum.Value, TestEnum.Value2}, options.getEnum()); |
| assertArrayEquals(new String[] {"abc", "beam"}, options.getStringValue().get()); |
| assertArrayEquals(new Long[] {123890123890L, 123890123891L}, options.getLongValue().get()); |
| assertArrayEquals( |
| new TestEnum[] {TestEnum.Value, TestEnum.Value2}, options.getEnumValue().get()); |
| } |
| |
| @Test |
| @SuppressWarnings("rawtypes") |
| public void testEmptyInStringArrays() { |
| String[] args = new String[] {"--string=", "--string=", "--string="}; |
| |
| Arrays options = PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| assertArrayEquals(new String[] {"", "", ""}, options.getString()); |
| } |
| |
| @Test |
| @SuppressWarnings("rawtypes") |
| public void testEmptyInStringArraysWithCommaList() { |
| String[] args = new String[] {"--string=a,,b"}; |
| |
| Arrays options = PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| assertArrayEquals(new String[] {"a", "", "b"}, options.getString()); |
| } |
| |
| @Test |
| public void testEmptyInNonStringArrays() { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| |
| String[] args = new String[] {"--boolean=true", "--boolean=", "--boolean=false"}; |
| |
| PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| } |
| |
| @Test |
| public void testEmptyInNonStringArraysWithCommaList() { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| |
| String[] args = new String[] {"--int=1,,9"}; |
| PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| } |
| |
| @Test |
| public void testStringArrayValueProvider() { |
| String[] args = new String[] {"--stringValue=abc", "--stringValue=xyz"}; |
| String[] commaArgs = new String[] {"--stringValue=abc,xyz"}; |
| String[] emptyArgs = new String[] {"--stringValue=", "--stringValue="}; |
| Arrays options = PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| assertArrayEquals(new String[] {"abc", "xyz"}, options.getStringValue().get()); |
| options = PipelineOptionsFactory.fromArgs(commaArgs).as(Arrays.class); |
| assertArrayEquals(new String[] {"abc", "xyz"}, options.getStringValue().get()); |
| options = PipelineOptionsFactory.fromArgs(emptyArgs).as(Arrays.class); |
| assertArrayEquals(new String[] {"", ""}, options.getStringValue().get()); |
| } |
| |
| @Test |
| public void testLongArrayValueProvider() { |
| String[] args = new String[] {"--longValue=12345678762", "--longValue=12345678763"}; |
| String[] commaArgs = new String[] {"--longValue=12345678762,12345678763"}; |
| String[] emptyArgs = new String[] {"--longValue=", "--longValue="}; |
| Arrays options = PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| assertArrayEquals(new Long[] {12345678762L, 12345678763L}, options.getLongValue().get()); |
| options = PipelineOptionsFactory.fromArgs(commaArgs).as(Arrays.class); |
| assertArrayEquals(new Long[] {12345678762L, 12345678763L}, options.getLongValue().get()); |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| PipelineOptionsFactory.fromArgs(emptyArgs).as(Arrays.class); |
| } |
| |
| @Test |
| public void testEnumArrayValueProvider() { |
| String[] args = |
| new String[] {"--enumValue=" + TestEnum.Value, "--enumValue=" + TestEnum.Value2}; |
| String[] commaArgs = new String[] {"--enumValue=" + TestEnum.Value + "," + TestEnum.Value2}; |
| String[] emptyArgs = new String[] {"--enumValue="}; |
| Arrays options = PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| assertArrayEquals( |
| new TestEnum[] {TestEnum.Value, TestEnum.Value2}, options.getEnumValue().get()); |
| options = PipelineOptionsFactory.fromArgs(commaArgs).as(Arrays.class); |
| assertArrayEquals( |
| new TestEnum[] {TestEnum.Value, TestEnum.Value2}, options.getEnumValue().get()); |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| PipelineOptionsFactory.fromArgs(emptyArgs).as(Arrays.class); |
| } |
| |
| @Test |
| public void testOutOfOrderArrays() { |
| String[] args = |
| new String[] { |
| "--char=d", "--boolean=true", "--boolean=true", "--char=e", "--char=f", "--boolean=false" |
| }; |
| |
| Arrays options = PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| boolean[] bools = options.getBoolean(); |
| assertTrue(bools[0] && bools[1] && !bools[2]); |
| assertArrayEquals(new char[] {'d', 'e', 'f'}, options.getChar()); |
| } |
| |
| /** A test interface containing all supported List return types. */ |
| public interface Lists extends PipelineOptions { |
| List<String> getString(); |
| |
| void setString(List<String> value); |
| |
| List<Integer> getInteger(); |
| |
| void setInteger(List<Integer> value); |
| |
| @SuppressWarnings("rawtypes") |
| List getList(); |
| |
| @SuppressWarnings("rawtypes") |
| void setList(List value); |
| |
| ValueProvider<List<String>> getStringValue(); |
| |
| void setStringValue(ValueProvider<List<String>> value); |
| |
| ValueProvider<List<Long>> getLongValue(); |
| |
| void setLongValue(ValueProvider<List<Long>> value); |
| |
| ValueProvider<List<TestEnum>> getEnumValue(); |
| |
| void setEnumValue(ValueProvider<List<TestEnum>> value); |
| } |
| |
| @Test |
| public void testListRawDefaultsToString() { |
| String[] manyArgs = |
| new String[] {"--list=stringValue1", "--list=stringValue2", "--list=stringValue3"}; |
| |
| String[] manyArgsWithEmptyString = |
| new String[] {"--list=stringValue1", "--list=", "--list=stringValue3"}; |
| |
| Lists options = PipelineOptionsFactory.fromArgs(manyArgs).as(Lists.class); |
| assertEquals( |
| ImmutableList.of("stringValue1", "stringValue2", "stringValue3"), options.getList()); |
| options = PipelineOptionsFactory.fromArgs(manyArgsWithEmptyString).as(Lists.class); |
| assertEquals(ImmutableList.of("stringValue1", "", "stringValue3"), options.getList()); |
| } |
| |
| @Test |
| public void testListString() { |
| String[] manyArgs = |
| new String[] {"--string=stringValue1", "--string=stringValue2", "--string=stringValue3"}; |
| String[] oneArg = new String[] {"--string=stringValue1"}; |
| String[] emptyArg = new String[] {"--string="}; |
| |
| Lists options = PipelineOptionsFactory.fromArgs(manyArgs).as(Lists.class); |
| assertEquals( |
| ImmutableList.of("stringValue1", "stringValue2", "stringValue3"), options.getString()); |
| |
| options = PipelineOptionsFactory.fromArgs(oneArg).as(Lists.class); |
| assertEquals(ImmutableList.of("stringValue1"), options.getString()); |
| |
| options = PipelineOptionsFactory.fromArgs(emptyArg).as(Lists.class); |
| assertEquals(ImmutableList.of(""), options.getString()); |
| } |
| |
| @Test |
| public void testListInt() { |
| String[] manyArgs = new String[] {"--integer=1", "--integer=2", "--integer=3"}; |
| String[] manyArgsShort = new String[] {"--integer=1,2,3"}; |
| String[] oneArg = new String[] {"--integer=1"}; |
| String[] missingArg = new String[] {"--integer="}; |
| |
| Lists options = PipelineOptionsFactory.fromArgs(manyArgs).as(Lists.class); |
| assertEquals(ImmutableList.of(1, 2, 3), options.getInteger()); |
| options = PipelineOptionsFactory.fromArgs(manyArgsShort).as(Lists.class); |
| assertEquals(ImmutableList.of(1, 2, 3), options.getInteger()); |
| options = PipelineOptionsFactory.fromArgs(oneArg).as(Lists.class); |
| assertEquals(ImmutableList.of(1), options.getInteger()); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage("java.util.List<java.lang.Integer>")); |
| PipelineOptionsFactory.fromArgs(missingArg).as(Lists.class); |
| } |
| |
| @Test |
| public void testListShorthand() { |
| String[] args = new String[] {"--string=stringValue1,stringValue2,stringValue3"}; |
| |
| Lists options = PipelineOptionsFactory.fromArgs(args).as(Lists.class); |
| assertEquals( |
| ImmutableList.of("stringValue1", "stringValue2", "stringValue3"), options.getString()); |
| } |
| |
| @Test |
| public void testMixedShorthandAndLongStyleList() { |
| String[] args = |
| new String[] { |
| "--char=d", |
| "--char=e", |
| "--char=f", |
| "--char=g,h,i", |
| "--char=j", |
| "--char=k", |
| "--char=l", |
| "--char=m,n,o" |
| }; |
| |
| Arrays options = PipelineOptionsFactory.fromArgs(args).as(Arrays.class); |
| assertArrayEquals( |
| new char[] {'d', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'}, options.getChar()); |
| } |
| |
| @Test |
| public void testStringListValueProvider() { |
| String[] args = new String[] {"--stringValue=abc", "--stringValue=xyz"}; |
| String[] commaArgs = new String[] {"--stringValue=abc,xyz"}; |
| String[] emptyArgs = new String[] {"--stringValue=", "--stringValue="}; |
| Lists options = PipelineOptionsFactory.fromArgs(args).as(Lists.class); |
| assertEquals(ImmutableList.of("abc", "xyz"), options.getStringValue().get()); |
| options = PipelineOptionsFactory.fromArgs(commaArgs).as(Lists.class); |
| assertEquals(ImmutableList.of("abc", "xyz"), options.getStringValue().get()); |
| options = PipelineOptionsFactory.fromArgs(emptyArgs).as(Lists.class); |
| assertEquals(ImmutableList.of("", ""), options.getStringValue().get()); |
| } |
| |
| @Test |
| public void testLongListValueProvider() { |
| String[] args = new String[] {"--longValue=12345678762", "--longValue=12345678763"}; |
| String[] commaArgs = new String[] {"--longValue=12345678762,12345678763"}; |
| String[] emptyArgs = new String[] {"--longValue=", "--longValue="}; |
| Lists options = PipelineOptionsFactory.fromArgs(args).as(Lists.class); |
| assertEquals(ImmutableList.of(12345678762L, 12345678763L), options.getLongValue().get()); |
| options = PipelineOptionsFactory.fromArgs(commaArgs).as(Lists.class); |
| assertEquals(ImmutableList.of(12345678762L, 12345678763L), options.getLongValue().get()); |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| PipelineOptionsFactory.fromArgs(emptyArgs).as(Lists.class); |
| } |
| |
| @Test |
| public void testEnumListValueProvider() { |
| String[] args = |
| new String[] {"--enumValue=" + TestEnum.Value, "--enumValue=" + TestEnum.Value2}; |
| String[] commaArgs = new String[] {"--enumValue=" + TestEnum.Value + "," + TestEnum.Value2}; |
| String[] emptyArgs = new String[] {"--enumValue="}; |
| Lists options = PipelineOptionsFactory.fromArgs(args).as(Lists.class); |
| assertEquals(ImmutableList.of(TestEnum.Value, TestEnum.Value2), options.getEnumValue().get()); |
| options = PipelineOptionsFactory.fromArgs(commaArgs).as(Lists.class); |
| assertEquals(ImmutableList.of(TestEnum.Value, TestEnum.Value2), options.getEnumValue().get()); |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage(emptyStringErrorMessage()); |
| PipelineOptionsFactory.fromArgs(emptyArgs).as(Lists.class); |
| } |
| |
| @Test |
| public void testSetASingularAttributeUsingAListThrowsAnError() { |
| String[] args = new String[] {"--string=100", "--string=200"}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("expected one element but was"); |
| PipelineOptionsFactory.fromArgs(args).as(Objects.class); |
| } |
| |
| @Test |
| public void testSetASingularAttributeUsingAListIsIgnoredWithoutStrictParsing() { |
| String[] args = new String[] {"--diskSizeGb=100", "--diskSizeGb=200"}; |
| PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().create(); |
| expectedLogs.verifyWarn("Strict parsing is disabled, ignoring option"); |
| } |
| |
| private interface NonPublicPipelineOptions extends PipelineOptions {} |
| |
| @Test |
| public void testNonPublicInterfaceThrowsException() throws Exception { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Please mark non-public interface " |
| + NonPublicPipelineOptions.class.getName() |
| + " as public."); |
| |
| PipelineOptionsFactory.as(NonPublicPipelineOptions.class); |
| } |
| |
| /** A test interface containing all supported List return types. */ |
| public interface Maps extends PipelineOptions { |
| Map<Integer, Integer> getMap(); |
| |
| void setMap(Map<Integer, Integer> value); |
| |
| Map<Integer, Map<Integer, Integer>> getNestedMap(); |
| |
| void setNestedMap(Map<Integer, Map<Integer, Integer>> value); |
| } |
| |
| @Test |
| public void testMapIntInt() { |
| String[] manyArgsShort = new String[] {"--map={\"1\":1,\"2\":2}"}; |
| String[] oneArg = new String[] {"--map={\"1\":1}"}; |
| String[] missingArg = new String[] {"--map="}; |
| |
| Maps options = PipelineOptionsFactory.fromArgs(manyArgsShort).as(Maps.class); |
| assertEquals(ImmutableMap.of(1, 1, 2, 2), options.getMap()); |
| options = PipelineOptionsFactory.fromArgs(oneArg).as(Maps.class); |
| assertEquals(ImmutableMap.of(1, 1), options.getMap()); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| emptyStringErrorMessage("java.util.Map<java.lang.Integer, java.lang.Integer>")); |
| PipelineOptionsFactory.fromArgs(missingArg).as(Maps.class); |
| } |
| |
| @Test |
| public void testNestedMap() { |
| String[] manyArgsShort = new String[] {"--nestedMap={\"1\":{\"1\":1},\"2\":{\"2\":2}}"}; |
| String[] oneArg = new String[] {"--nestedMap={\"1\":{\"1\":1}}"}; |
| String[] missingArg = new String[] {"--nestedMap="}; |
| |
| Maps options = PipelineOptionsFactory.fromArgs(manyArgsShort).as(Maps.class); |
| assertEquals( |
| ImmutableMap.of( |
| 1, ImmutableMap.of(1, 1), |
| 2, ImmutableMap.of(2, 2)), |
| options.getNestedMap()); |
| options = PipelineOptionsFactory.fromArgs(oneArg).as(Maps.class); |
| assertEquals(ImmutableMap.of(1, ImmutableMap.of(1, 1)), options.getNestedMap()); |
| |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| emptyStringErrorMessage( |
| "java.util.Map<java.lang.Integer, java.util.Map<java.lang.Integer, java.lang.Integer>>")); |
| PipelineOptionsFactory.fromArgs(missingArg).as(Maps.class); |
| } |
| |
| @Test |
| public void testSettingRunner() { |
| String[] args = new String[] {"--runner=" + RegisteredTestRunner.class.getSimpleName()}; |
| |
| PipelineOptions options = PipelineOptionsFactory.fromArgs(args).create(); |
| assertEquals(RegisteredTestRunner.class, options.getRunner()); |
| } |
| |
| @Test |
| public void testSettingRunnerFullName() { |
| String[] args = new String[] {String.format("--runner=%s", CrashingRunner.class.getName())}; |
| PipelineOptions opts = PipelineOptionsFactory.fromArgs(args).create(); |
| assertEquals(opts.getRunner(), CrashingRunner.class); |
| } |
| |
| @Test |
| public void testSettingUnknownRunner() { |
| String[] args = new String[] {"--runner=UnknownRunner"}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "Unknown 'runner' specified 'UnknownRunner', supported " + "pipeline runners"); |
| Set<String> registeredRunners = PipelineOptionsFactory.getRegisteredRunners().keySet(); |
| assertThat(registeredRunners, hasItem(REGISTERED_RUNNER.getSimpleName().toLowerCase())); |
| |
| expectedException.expectMessage( |
| PipelineOptionsFactory.CACHE.get().getSupportedRunners().toString()); |
| |
| PipelineOptionsFactory.fromArgs(args).create(); |
| } |
| |
| private static class ExampleTestRunner extends PipelineRunner<PipelineResult> { |
| @Override |
| public PipelineResult run(Pipeline pipeline) { |
| return null; |
| } |
| } |
| |
| @Test |
| public void testSettingRunnerCanonicalClassNameNotInSupportedExists() { |
| String[] args = new String[] {String.format("--runner=%s", ExampleTestRunner.class.getName())}; |
| PipelineOptions opts = PipelineOptionsFactory.fromArgs(args).create(); |
| assertEquals(opts.getRunner(), ExampleTestRunner.class); |
| } |
| |
| @Test |
| public void testSettingRunnerCanonicalClassNameNotInSupportedNotPipelineRunner() { |
| String[] args = new String[] {"--runner=java.lang.String"}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("does not implement PipelineRunner"); |
| expectedException.expectMessage("java.lang.String"); |
| |
| PipelineOptionsFactory.fromArgs(args).create(); |
| } |
| |
| @Test |
| public void testUsingArgumentWithUnknownPropertyIsNotAllowed() { |
| String[] args = new String[] {"--unknownProperty=value"}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("missing a property named 'unknownProperty'"); |
| PipelineOptionsFactory.fromArgs(args).create(); |
| } |
| |
| /** Test interface. */ |
| public interface SuggestedOptions extends PipelineOptions { |
| String getAbc(); |
| |
| void setAbc(String value); |
| |
| String getAbcdefg(); |
| |
| void setAbcdefg(String value); |
| } |
| |
| @Test |
| public void testUsingArgumentWithMisspelledPropertyGivesASuggestion() { |
| String[] args = new String[] {"--ab=value"}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("missing a property named 'ab'. Did you mean 'abc'?"); |
| PipelineOptionsFactory.fromArgs(args).as(SuggestedOptions.class); |
| } |
| |
| @Test |
| public void testUsingArgumentWithMisspelledPropertyGivesMultipleSuggestions() { |
| String[] args = new String[] {"--abcde=value"}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "missing a property named 'abcde'. Did you mean one of [abc, abcdefg]?"); |
| PipelineOptionsFactory.fromArgs(args).as(SuggestedOptions.class); |
| } |
| |
| @Test |
| public void testUsingArgumentWithUnknownPropertyIsIgnoredWithoutStrictParsing() { |
| String[] args = new String[] {"--unknownProperty=value"}; |
| PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().create(); |
| expectedLogs.verifyWarn("missing a property named 'unknownProperty'"); |
| } |
| |
| @Test |
| public void testUsingArgumentStartingWithIllegalCharacterIsNotAllowed() { |
| String[] args = new String[] {" --diskSizeGb=100"}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("Argument ' --diskSizeGb=100' does not begin with '--'"); |
| PipelineOptionsFactory.fromArgs(args).create(); |
| } |
| |
| @Test |
| public void testUsingArgumentStartingWithIllegalCharacterIsIgnoredWithoutStrictParsing() { |
| String[] args = new String[] {" --diskSizeGb=100"}; |
| PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().create(); |
| expectedLogs.verifyWarn("Strict parsing is disabled, ignoring option"); |
| } |
| |
| @Test |
| public void testEmptyArgumentIsIgnored() { |
| String[] args = |
| new String[] {"", "--string=100", "", "", "--runner=" + REGISTERED_RUNNER.getSimpleName()}; |
| PipelineOptionsFactory.fromArgs(args).as(Objects.class); |
| } |
| |
| @Test |
| public void testNullArgumentIsIgnored() { |
| String[] args = |
| new String[] {"--string=100", null, null, "--runner=" + REGISTERED_RUNNER.getSimpleName()}; |
| PipelineOptionsFactory.fromArgs(args).as(Objects.class); |
| } |
| |
| @Test |
| public void testUsingArgumentWithInvalidNameIsNotAllowed() { |
| String[] args = new String[] {"--=100"}; |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage("Argument '--=100' starts with '--='"); |
| PipelineOptionsFactory.fromArgs(args).create(); |
| } |
| |
| @Test |
| public void testUsingArgumentWithInvalidNameIsIgnoredWithoutStrictParsing() { |
| String[] args = new String[] {"--=100"}; |
| PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().create(); |
| expectedLogs.verifyWarn("Strict parsing is disabled, ignoring option"); |
| } |
| |
| @Test |
| public void testWhenNoHelpIsRequested() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| assertFalse( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertEquals("", output); |
| } |
| |
| @Test |
| public void testDefaultHelpAsArgument() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| arguments.put("help", "true"); |
| assertTrue( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("The set of registered options are:")); |
| assertThat(output, containsString("org.apache.beam.sdk.options.PipelineOptions")); |
| assertThat(output, containsString("Use --help=<OptionsName> for detailed help.")); |
| } |
| |
| @Test |
| public void testSpecificHelpAsArgument() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| arguments.put("help", "org.apache.beam.sdk.options.PipelineOptions"); |
| assertTrue( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("org.apache.beam.sdk.options.PipelineOptions")); |
| assertThat(output, containsString("--runner")); |
| assertThat(output, containsString("Default: " + DEFAULT_RUNNER_NAME)); |
| assertThat( |
| output, containsString("The pipeline runner that will be used to execute the pipeline.")); |
| } |
| |
| @Test |
| public void testSpecificHelpAsArgumentWithSimpleClassName() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| arguments.put("help", "PipelineOptions"); |
| assertTrue( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("org.apache.beam.sdk.options.PipelineOptions")); |
| assertThat(output, containsString("--runner")); |
| assertThat(output, containsString("Default: " + DEFAULT_RUNNER_NAME)); |
| assertThat( |
| output, containsString("The pipeline runner that will be used to execute the pipeline.")); |
| } |
| |
| @Test |
| public void testSpecificHelpAsArgumentWithClassNameSuffix() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| arguments.put("help", "options.PipelineOptions"); |
| assertTrue( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("org.apache.beam.sdk.options.PipelineOptions")); |
| assertThat(output, containsString("--runner")); |
| assertThat(output, containsString("Default: " + DEFAULT_RUNNER_NAME)); |
| assertThat( |
| output, containsString("The pipeline runner that will be used to execute the pipeline.")); |
| } |
| |
| /** Used for a name collision test with the other NameConflict interfaces. */ |
| public static class NameConflictClassA { |
| /** Used for a name collision test with the other NameConflict interfaces. */ |
| public interface NameConflict extends PipelineOptions {} |
| } |
| |
| /** Used for a name collision test with the other NameConflict interfaces. */ |
| public static class NameConflictClassB { |
| /** Used for a name collision test with the other NameConflict interfaces. */ |
| public interface NameConflict extends PipelineOptions {} |
| } |
| |
| @Test |
| public void testShortnameSpecificHelpHasMultipleMatches() { |
| PipelineOptionsFactory.register(NameConflictClassA.NameConflict.class); |
| PipelineOptionsFactory.register(NameConflictClassB.NameConflict.class); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| arguments.put("help", "NameConflict"); |
| assertTrue( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("Multiple matches found for NameConflict")); |
| assertThat( |
| output, |
| containsString( |
| "org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$NameConflictClassA$NameConflict")); |
| assertThat( |
| output, |
| containsString( |
| "org.apache.beam.sdk.options." |
| + "PipelineOptionsFactoryTest$NameConflictClassB$NameConflict")); |
| assertThat(output, containsString("The set of registered options are:")); |
| assertThat(output, containsString("org.apache.beam.sdk.options.PipelineOptions")); |
| } |
| |
| @Test |
| public void testHelpWithOptionThatOutputsValidEnumTypes() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| arguments.put("help", Objects.class.getName()); |
| assertTrue( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("<Value | Value2>")); |
| } |
| |
| @Test |
| public void testHelpWithBadOptionNameAsArgument() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| arguments.put("help", "org.apache.beam.sdk.Pipeline"); |
| assertTrue( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("Unable to find option org.apache.beam.sdk.Pipeline")); |
| assertThat(output, containsString("The set of registered options are:")); |
| assertThat(output, containsString("org.apache.beam.sdk.options.PipelineOptions")); |
| } |
| |
| @Test |
| public void testHelpWithHiddenMethodAndInterface() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ListMultimap<String, String> arguments = ArrayListMultimap.create(); |
| arguments.put("help", "org.apache.beam.sdk.option.DataflowPipelineOptions"); |
| assertTrue( |
| PipelineOptionsFactory.printHelpUsageAndExitIfNeeded( |
| arguments, new PrintStream(baos), false /* exit */)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| // A hidden interface. |
| assertThat( |
| output, not(containsString("org.apache.beam.sdk.options.DataflowPipelineDebugOptions"))); |
| // A hidden option. |
| assertThat(output, not(containsString("--gcpCredential"))); |
| } |
| |
| @Test |
| public void testProgrammaticPrintHelp() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| PipelineOptionsFactory.printHelp(new PrintStream(baos)); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("The set of registered options are:")); |
| assertThat(output, containsString("org.apache.beam.sdk.options.PipelineOptions")); |
| } |
| |
| @Test |
| public void testProgrammaticPrintHelpForSpecificType() { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| PipelineOptionsFactory.printHelp(new PrintStream(baos), PipelineOptions.class); |
| String output = new String(baos.toByteArray(), Charsets.UTF_8); |
| assertThat(output, containsString("org.apache.beam.sdk.options.PipelineOptions")); |
| assertThat(output, containsString("--runner")); |
| assertThat(output, containsString("Default: " + DEFAULT_RUNNER_NAME)); |
| assertThat( |
| output, containsString("The pipeline runner that will be used to execute the pipeline.")); |
| } |
| |
| /** Test interface. */ |
| public interface PipelineOptionsInheritedInvalid |
| extends Invalid1, InvalidPipelineOptions2, PipelineOptions { |
| String getFoo(); |
| |
| void setFoo(String value); |
| } |
| |
| /** Test interface. */ |
| public interface InvalidPipelineOptions1 { |
| String getBar(); |
| |
| void setBar(String value); |
| } |
| |
| /** Test interface. */ |
| public interface Invalid1 extends InvalidPipelineOptions1 { |
| @Override |
| String getBar(); |
| |
| @Override |
| void setBar(String value); |
| } |
| |
| /** Test interface. */ |
| public interface InvalidPipelineOptions2 { |
| String getBar(); |
| |
| void setBar(String value); |
| } |
| |
| @Test |
| public void testAllFromPipelineOptions() { |
| expectedException.expect(IllegalArgumentException.class); |
| expectedException.expectMessage( |
| "All inherited interfaces of [org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$PipelineOptionsInheritedInvalid] should inherit from the PipelineOptions interface. " |
| + "The following inherited interfaces do not:\n" |
| + " - org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$InvalidPipelineOptions1\n" |
| + " - org.apache.beam.sdk.options.PipelineOptionsFactoryTest" |
| + "$InvalidPipelineOptions2"); |
| |
| PipelineOptionsFactory.as(PipelineOptionsInheritedInvalid.class); |
| } |
| |
| private String emptyStringErrorMessage() { |
| return emptyStringErrorMessage(null); |
| } |
| |
| private String emptyStringErrorMessage(String type) { |
| String msg = |
| "Empty argument value is only allowed for String, String Array, " |
| + "Collections of Strings or any of these types in a parameterized ValueProvider"; |
| if (type != null) { |
| return String.format("%s, but received: %s", msg, type); |
| } else { |
| return msg; |
| } |
| } |
| |
| private static class RegisteredTestRunner extends PipelineRunner<PipelineResult> { |
| public static PipelineRunner<PipelineResult> fromOptions(PipelineOptions options) { |
| return new RegisteredTestRunner(); |
| } |
| |
| @Override |
| public PipelineResult run(Pipeline p) { |
| throw new IllegalArgumentException(); |
| } |
| } |
| |
| /** |
| * A {@link PipelineRunnerRegistrar} to demonstrate default {@link PipelineRunner} registration. |
| */ |
| @AutoService(PipelineRunnerRegistrar.class) |
| public static class RegisteredTestRunnerRegistrar implements PipelineRunnerRegistrar { |
| @Override |
| public Iterable<Class<? extends PipelineRunner<?>>> getPipelineRunners() { |
| return ImmutableList.of(RegisteredTestRunner.class); |
| } |
| } |
| |
| /** Test interface. */ |
| public interface RegisteredTestOptions extends PipelineOptions { |
| Object getRegisteredExampleFooBar(); |
| |
| void setRegisteredExampleFooBar(Object registeredExampleFooBar); |
| } |
| |
| /** |
| * A {@link PipelineOptionsRegistrar} to demonstrate default {@link PipelineOptions} registration. |
| */ |
| @AutoService(PipelineOptionsRegistrar.class) |
| public static class RegisteredTestOptionsRegistrar implements PipelineOptionsRegistrar { |
| @Override |
| public Iterable<Class<? extends PipelineOptions>> getPipelineOptions() { |
| return ImmutableList.of(RegisteredTestOptions.class); |
| } |
| } |
| |
| @Test |
| public void testRegistrationOfJacksonModulesForObjectMapper() throws Exception { |
| JacksonIncompatibleOptions options = |
| PipelineOptionsFactory.fromArgs("--jacksonIncompatible=\"testValue\"") |
| .as(JacksonIncompatibleOptions.class); |
| assertEquals("testValue", options.getJacksonIncompatible().value); |
| } |
| |
| /** PipelineOptions used to test auto registration of Jackson modules. */ |
| public interface JacksonIncompatibleOptions extends PipelineOptions { |
| JacksonIncompatible getJacksonIncompatible(); |
| |
| void setJacksonIncompatible(JacksonIncompatible value); |
| } |
| |
| /** A Jackson {@link Module} to test auto-registration of modules. */ |
| @AutoService(Module.class) |
| public static class RegisteredTestModule extends SimpleModule { |
| public RegisteredTestModule() { |
| super("RegisteredTestModule"); |
| setMixInAnnotation(JacksonIncompatible.class, JacksonIncompatibleMixin.class); |
| } |
| } |
| |
| /** A class which Jackson does not know how to serialize/deserialize. */ |
| public static class JacksonIncompatible { |
| private final String value; |
| |
| public JacksonIncompatible(String value) { |
| this.value = value; |
| } |
| } |
| |
| /** A Jackson mixin used to add annotations to other classes. */ |
| @JsonDeserialize(using = JacksonIncompatibleDeserializer.class) |
| @JsonSerialize(using = JacksonIncompatibleSerializer.class) |
| public static final class JacksonIncompatibleMixin {} |
| |
| /** A Jackson deserializer for {@link JacksonIncompatible}. */ |
| public static class JacksonIncompatibleDeserializer |
| extends JsonDeserializer<JacksonIncompatible> { |
| |
| @Override |
| public JacksonIncompatible deserialize( |
| JsonParser jsonParser, DeserializationContext deserializationContext) |
| throws IOException, JsonProcessingException { |
| return new JacksonIncompatible(jsonParser.readValueAs(String.class)); |
| } |
| } |
| |
| /** A Jackson serializer for {@link JacksonIncompatible}. */ |
| public static class JacksonIncompatibleSerializer extends JsonSerializer<JacksonIncompatible> { |
| |
| @Override |
| public void serialize( |
| JacksonIncompatible jacksonIncompatible, |
| JsonGenerator jsonGenerator, |
| SerializerProvider serializerProvider) |
| throws IOException, JsonProcessingException { |
| jsonGenerator.writeString(jacksonIncompatible.value); |
| } |
| } |
| |
| /** Used to test that the thread context class loader is used when creating proxies. */ |
| public interface ClassLoaderTestOptions extends PipelineOptions { |
| @Default.Boolean(true) |
| @Description("A test option.") |
| boolean isOption(); |
| |
| void setOption(boolean b); |
| } |
| |
| @Test |
| public void testPipelineOptionsFactoryUsesTccl() throws Exception { |
| final Thread thread = Thread.currentThread(); |
| final ClassLoader testClassLoader = thread.getContextClassLoader(); |
| final ClassLoader caseLoader = |
| new InterceptingUrlClassLoader( |
| testClassLoader, name -> name.toLowerCase(ROOT).contains("test")); |
| thread.setContextClassLoader(caseLoader); |
| PipelineOptionsFactory.resetCache(); |
| try { |
| final PipelineOptions pipelineOptions = PipelineOptionsFactory.create(); |
| final Class optionType = |
| caseLoader.loadClass( |
| "org.apache.beam.sdk.options.PipelineOptionsFactoryTest$ClassLoaderTestOptions"); |
| final Object options = pipelineOptions.as(optionType); |
| assertSame(caseLoader, options.getClass().getClassLoader()); |
| assertSame(optionType.getClassLoader(), options.getClass().getClassLoader()); |
| assertSame(testClassLoader, optionType.getInterfaces()[0].getClassLoader()); |
| assertTrue(Boolean.class.cast(optionType.getMethod("isOption").invoke(options))); |
| } finally { |
| thread.setContextClassLoader(testClassLoader); |
| PipelineOptionsFactory.resetCache(); |
| } |
| } |
| |
| @Test |
| public void testDefaultMethodIgnoresDefaultImplementation() { |
| OptionsWithDefaultMethod optsWithDefault = |
| PipelineOptionsFactory.as(OptionsWithDefaultMethod.class); |
| assertThat(optsWithDefault.getValue(), nullValue()); |
| |
| optsWithDefault.setValue(12.25); |
| assertThat(optsWithDefault.getValue(), equalTo(12.25)); |
| } |
| |
| /** Test interface. */ |
| public interface ExtendedOptionsWithDefault extends OptionsWithDefaultMethod {} |
| |
| @Test |
| public void testDefaultMethodInExtendedClassIgnoresDefaultImplementation() { |
| OptionsWithDefaultMethod extendedOptsWithDefault = |
| PipelineOptionsFactory.as(ExtendedOptionsWithDefault.class); |
| assertThat(extendedOptsWithDefault.getValue(), nullValue()); |
| |
| extendedOptsWithDefault.setValue(Double.NEGATIVE_INFINITY); |
| assertThat(extendedOptsWithDefault.getValue(), equalTo(Double.NEGATIVE_INFINITY)); |
| } |
| |
| /** Test interface. */ |
| public interface OptionsWithDefaultMethod extends PipelineOptions { |
| default Number getValue() { |
| return 1024; |
| } |
| |
| void setValue(Number value); |
| } |
| |
| @Test |
| public void testStaticMethodsAreAllowed() { |
| assertEquals( |
| "value", |
| OptionsWithStaticMethod.myStaticMethod( |
| PipelineOptionsFactory.fromArgs("--myMethod=value").as(OptionsWithStaticMethod.class))); |
| } |
| |
| /** Test interface. */ |
| public interface OptionsWithStaticMethod extends PipelineOptions { |
| String getMyMethod(); |
| |
| void setMyMethod(String value); |
| |
| static String myStaticMethod(OptionsWithStaticMethod o) { |
| return o.getMyMethod(); |
| } |
| } |
| |
| /** Test interface. */ |
| public interface TestDescribeOptions extends PipelineOptions { |
| String getString(); |
| |
| void setString(String value); |
| |
| @Description("integer property") |
| Integer getInteger(); |
| |
| void setInteger(Integer value); |
| |
| @Description("float number property") |
| Float getFloat(); |
| |
| void setFloat(Float value); |
| |
| @Description("simple boolean property") |
| @Default.Boolean(true) |
| boolean getBooleanSimple(); |
| |
| void setBooleanSimple(boolean value); |
| |
| @Default.Boolean(false) |
| Boolean getBooleanWrapper(); |
| |
| void setBooleanWrapper(Boolean value); |
| |
| List<Integer> getList(); |
| |
| void setList(List<Integer> value); |
| } |
| |
| @Test |
| public void testDescribe() { |
| List<PipelineOptionDescriptor> described = |
| PipelineOptionsFactory.describe( |
| Sets.newHashSet(PipelineOptions.class, TestDescribeOptions.class)); |
| |
| Map<String, PipelineOptionDescriptor> mapped = uniqueIndex(described, input -> input.getName()); |
| assertEquals("no duplicates", described.size(), mapped.size()); |
| |
| Collection<PipelineOptionDescriptor> filtered = |
| Collections2.filter( |
| described, input -> input.getGroup().equals(TestDescribeOptions.class.getName())); |
| assertEquals(6, filtered.size()); |
| mapped = uniqueIndex(filtered, input -> input.getName()); |
| |
| PipelineOptionDescriptor listDesc = mapped.get("list"); |
| assertThat(listDesc, notNullValue()); |
| assertThat(listDesc.getDescription(), isEmptyString()); |
| assertEquals(PipelineOptionType.Enum.ARRAY, listDesc.getType()); |
| assertThat(listDesc.getDefaultValue(), isEmptyString()); |
| |
| PipelineOptionDescriptor stringDesc = mapped.get("string"); |
| assertThat(stringDesc, notNullValue()); |
| assertThat(stringDesc.getDescription(), isEmptyString()); |
| assertEquals(PipelineOptionType.Enum.STRING, stringDesc.getType()); |
| assertThat(stringDesc.getDefaultValue(), isEmptyString()); |
| |
| PipelineOptionDescriptor integerDesc = mapped.get("integer"); |
| assertThat(integerDesc, notNullValue()); |
| assertEquals("integer property", integerDesc.getDescription()); |
| assertEquals(PipelineOptionType.Enum.INTEGER, integerDesc.getType()); |
| assertThat(integerDesc.getDefaultValue(), isEmptyString()); |
| |
| PipelineOptionDescriptor floatDesc = mapped.get("float"); |
| assertThat(integerDesc, notNullValue()); |
| assertEquals("float number property", floatDesc.getDescription()); |
| assertEquals(PipelineOptionType.Enum.NUMBER, floatDesc.getType()); |
| assertThat(floatDesc.getDefaultValue(), isEmptyString()); |
| |
| PipelineOptionDescriptor booleanSimpleDesc = mapped.get("boolean_simple"); |
| assertThat(booleanSimpleDesc, notNullValue()); |
| assertEquals("simple boolean property", booleanSimpleDesc.getDescription()); |
| assertEquals(PipelineOptionType.Enum.BOOLEAN, booleanSimpleDesc.getType()); |
| assertThat(booleanSimpleDesc.getDefaultValue(), equalTo("true")); |
| |
| PipelineOptionDescriptor booleanWrapperDesc = mapped.get("boolean_wrapper"); |
| assertThat(booleanWrapperDesc, notNullValue()); |
| assertThat(booleanWrapperDesc.getDescription(), isEmptyString()); |
| assertEquals(PipelineOptionType.Enum.BOOLEAN, booleanWrapperDesc.getType()); |
| assertThat(booleanWrapperDesc.getDefaultValue(), equalTo("false")); |
| } |
| } |