blob: a98d0276c494a7b8e4a9ea9eb2e5099d7dfc37aa [file] [log] [blame]
/*
* 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 org.apache.beam.sdk.testing.CrashingRunner;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link PipelineOptionsValidator}. */
@RunWith(JUnit4.class)
public class PipelineOptionsValidatorTest {
@Rule public ExpectedException expectedException = ExpectedException.none();
/** A test interface with an {@link Validation.Required} annotation. */
public interface Required extends PipelineOptions {
@Validation.Required
@Description("Fake Description")
String getObject();
void setObject(String value);
}
@Test
public void testWhenRequiredOptionIsSet() {
Required required = PipelineOptionsFactory.as(Required.class);
required.setRunner(CrashingRunner.class);
required.setObject("blah");
PipelineOptionsValidator.validate(Required.class, required);
}
@Test
public void testWhenRequiredOptionIsSetAndCleared() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(
"Missing required value for "
+ "[public abstract java.lang.String org.apache.beam."
+ "sdk.options.PipelineOptionsValidatorTest$Required.getObject(), \"Fake Description\"].");
Required required = PipelineOptionsFactory.as(Required.class);
required.setObject("blah");
required.setObject(null);
PipelineOptionsValidator.validate(Required.class, required);
}
@Test
public void testWhenRequiredOptionIsSetAndClearedCli() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(
"Missing required value for " + "[--object, \"Fake Description\"].");
Required required =
PipelineOptionsFactory.fromArgs(new String[] {"--object=blah"}).as(Required.class);
required.setObject(null);
PipelineOptionsValidator.validateCli(Required.class, required);
}
@Test
public void testWhenRequiredOptionIsNeverSet() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(
"Missing required value for "
+ "[public abstract java.lang.String org.apache.beam."
+ "sdk.options.PipelineOptionsValidatorTest$Required.getObject(), \"Fake Description\"].");
Required required = PipelineOptionsFactory.as(Required.class);
PipelineOptionsValidator.validate(Required.class, required);
}
@Test
public void testWhenRequiredOptionIsNeverSetCli() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(
"Missing required value for " + "[--object, \"Fake Description\"].");
Required required = PipelineOptionsFactory.fromArgs(new String[] {}).as(Required.class);
PipelineOptionsValidator.validateCli(Required.class, required);
}
@Test
public void testWhenRequiredOptionIsNeverSetOnSuperInterface() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(
"Missing required value for "
+ "[public abstract java.lang.String org.apache.beam."
+ "sdk.options.PipelineOptionsValidatorTest$Required.getObject(), \"Fake Description\"].");
PipelineOptions options = PipelineOptionsFactory.create();
PipelineOptionsValidator.validate(Required.class, options);
}
@Test
public void testWhenRequiredOptionIsNeverSetOnSuperInterfaceCli() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(
"Missing required value for " + "[--object, \"Fake Description\"].");
PipelineOptions options = PipelineOptionsFactory.fromArgs(new String[] {}).create();
PipelineOptionsValidator.validateCli(Required.class, options);
}
/** A test interface that overrides the parent's method. */
public interface SubClassValidation extends Required {
@Override
String getObject();
@Override
void setObject(String value);
}
@Test
public void testValidationOnOverriddenMethods() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(
"Missing required value for "
+ "[public abstract java.lang.String org.apache.beam."
+ "sdk.options.PipelineOptionsValidatorTest$Required.getObject(), \"Fake Description\"].");
SubClassValidation required = PipelineOptionsFactory.as(SubClassValidation.class);
PipelineOptionsValidator.validate(Required.class, required);
}
@Test
public void testValidationOnOverriddenMethodsCli() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(
"Missing required value for " + "[--object, \"Fake Description\"].");
SubClassValidation required =
PipelineOptionsFactory.fromArgs(new String[] {}).as(SubClassValidation.class);
PipelineOptionsValidator.validateCli(Required.class, required);
}
/** A test interface with a required group. */
public interface GroupRequired extends PipelineOptions {
@Validation.Required(groups = {"ham"})
String getFoo();
void setFoo(String foo);
@Validation.Required(groups = {"ham"})
String getBar();
void setBar(String bar);
}
@Test
public void testWhenOneOfRequiredGroupIsSetIsValid() {
GroupRequired groupRequired = PipelineOptionsFactory.as(GroupRequired.class);
groupRequired.setFoo("foo");
groupRequired.setBar(null);
groupRequired.setRunner(CrashingRunner.class);
PipelineOptionsValidator.validate(GroupRequired.class, groupRequired);
// Symmetric
groupRequired.setFoo(null);
groupRequired.setBar("bar");
PipelineOptionsValidator.validate(GroupRequired.class, groupRequired);
}
@Test
public void testWhenNoneOfRequiredGroupIsSetThrowsException() {
GroupRequired groupRequired = PipelineOptionsFactory.as(GroupRequired.class);
groupRequired.setRunner(CrashingRunner.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Missing required value for group [ham]");
expectedException.expectMessage("properties");
expectedException.expectMessage("getFoo");
expectedException.expectMessage("getBar");
PipelineOptionsValidator.validate(GroupRequired.class, groupRequired);
}
/** A test interface with a member in multiple required groups. */
public interface MultiGroupRequired extends PipelineOptions {
@Validation.Required(groups = {"spam", "ham"})
String getFoo();
void setFoo(String foo);
@Validation.Required(groups = {"spam"})
String getBar();
void setBar(String bar);
@Validation.Required(groups = {"ham"})
String getBaz();
void setBaz(String baz);
}
@Test
public void testWhenOneOfMultipleRequiredGroupsIsSetIsValid() {
MultiGroupRequired multiGroupRequired = PipelineOptionsFactory.as(MultiGroupRequired.class);
multiGroupRequired.setRunner(CrashingRunner.class);
multiGroupRequired.setFoo("eggs");
PipelineOptionsValidator.validate(MultiGroupRequired.class, multiGroupRequired);
}
/** Test interface. */
public interface LeftOptions extends PipelineOptions {
@Validation.Required(groups = {"left"})
String getFoo();
void setFoo(String foo);
@Validation.Required(groups = {"left"})
String getLeft();
void setLeft(String left);
@Validation.Required(groups = {"both"})
String getBoth();
void setBoth(String both);
}
/** Test interface. */
public interface RightOptions extends PipelineOptions {
@Validation.Required(groups = {"right"})
String getFoo();
void setFoo(String foo);
@Validation.Required(groups = {"right"})
String getRight();
void setRight(String right);
@Validation.Required(groups = {"both"})
String getBoth();
void setBoth(String both);
}
/** Test interface. */
public interface JoinedOptions extends LeftOptions, RightOptions {}
@Test
public void testWhenOptionIsDefinedInMultipleSuperInterfacesAndIsNotPresentFailsRequirement() {
RightOptions rightOptions = PipelineOptionsFactory.as(RightOptions.class);
rightOptions.setBoth("foo");
rightOptions.setRunner(CrashingRunner.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Missing required value for group");
expectedException.expectMessage("getFoo");
PipelineOptionsValidator.validate(JoinedOptions.class, rightOptions);
}
@Test
public void testWhenOptionIsDefinedInMultipleSuperInterfacesMeetsGroupRequirement() {
RightOptions rightOpts = PipelineOptionsFactory.as(RightOptions.class);
rightOpts.setFoo("true");
rightOpts.setBoth("bar");
LeftOptions leftOpts = PipelineOptionsFactory.as(LeftOptions.class);
leftOpts.setFoo("Untrue");
leftOpts.setBoth("Raise the");
rightOpts.setRunner(CrashingRunner.class);
leftOpts.setRunner(CrashingRunner.class);
PipelineOptionsValidator.validate(JoinedOptions.class, rightOpts);
PipelineOptionsValidator.validate(JoinedOptions.class, leftOpts);
}
@Test
public void testWhenOptionIsDefinedOnOtherOptionsClassMeetsGroupRequirement() {
RightOptions rightOpts = PipelineOptionsFactory.as(RightOptions.class);
rightOpts.setFoo("true");
rightOpts.setBoth("bar");
LeftOptions leftOpts = PipelineOptionsFactory.as(LeftOptions.class);
leftOpts.setFoo("Untrue");
leftOpts.setBoth("Raise the");
rightOpts.setRunner(CrashingRunner.class);
leftOpts.setRunner(CrashingRunner.class);
PipelineOptionsValidator.validate(RightOptions.class, leftOpts);
PipelineOptionsValidator.validate(LeftOptions.class, rightOpts);
}
@Test
public void testWhenOptionIsDefinedOnMultipleInterfacesOnlyListedOnceWhenNotPresent() {
JoinedOptions options = PipelineOptionsFactory.as(JoinedOptions.class);
options.setFoo("Hello");
options.setRunner(CrashingRunner.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("required value for group [both]");
expectedException.expectMessage("properties [getBoth()]");
PipelineOptionsValidator.validate(JoinedOptions.class, options);
}
/** Test interface. */
public interface SuperOptions extends PipelineOptions {
@Validation.Required(groups = {"super"})
String getFoo();
void setFoo(String foo);
@Validation.Required(groups = {"sub"})
String getBar();
void setBar(String bar);
@Validation.Required(groups = {"otherSuper"})
String getSuperclassObj();
void setSuperclassObj(String sup);
}
/** Test interface. */
public interface SubOptions extends SuperOptions {
@Override
@Validation.Required(groups = {"sub"})
String getFoo();
@Override
void setFoo(String foo);
@Override
String getSuperclassObj();
@Override
void setSuperclassObj(String sup);
}
@Test
public void testSuperInterfaceRequiredOptionsAlsoRequiredInSubInterface() {
SubOptions subOpts = PipelineOptionsFactory.as(SubOptions.class);
subOpts.setFoo("Bar");
subOpts.setRunner(CrashingRunner.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("otherSuper");
expectedException.expectMessage("Missing required value");
expectedException.expectMessage("getSuperclassObj");
PipelineOptionsValidator.validate(SubOptions.class, subOpts);
}
@Test
public void
testSuperInterfaceGroupIsInAdditionToSubInterfaceGroupOnlyWhenValidatingSuperInterface() {
SubOptions opts = PipelineOptionsFactory.as(SubOptions.class);
opts.setFoo("Foo");
opts.setSuperclassObj("Hello world");
opts.setRunner(CrashingRunner.class);
// Valid SubOptions, but invalid SuperOptions
PipelineOptionsValidator.validate(SubOptions.class, opts);
expectedException.expectMessage("sub");
expectedException.expectMessage("Missing required value");
expectedException.expectMessage("getBar");
PipelineOptionsValidator.validate(SuperOptions.class, opts);
}
@Test
public void testSuperInterfaceRequiredOptionsSatisfiedBySubInterface() {
SubOptions subOpts = PipelineOptionsFactory.as(SubOptions.class);
subOpts.setFoo("bar");
subOpts.setBar("bar");
subOpts.setSuperclassObj("SuperDuper");
subOpts.setRunner(CrashingRunner.class);
PipelineOptionsValidator.validate(SubOptions.class, subOpts);
PipelineOptionsValidator.validate(SuperOptions.class, subOpts);
}
}