From f2bfe3f51b802b9900740dbe73253e47e1782c52 Mon Sep 17 00:00:00 2001 From: "ah.jo" Date: Sat, 24 Feb 2024 14:50:14 +0900 Subject: [PATCH] Add supporting for sealed class and sealed interface --- docs/content/v1.0.x/release-notes/_index.md | 6 ++ .../option/FixtureMonkeyOptionsBuilder.java | 3 + .../api/option/JdkVariantOptions.java | 29 +++++ .../SealedTypeObjectPropertyGenerator.java | 58 ++++++++++ .../api/option/JdkVariantOptions.java | 39 +++++++ .../tests/java17/SealedClassTest.java | 100 ++++++++++++++++++ 6 files changed, 235 insertions(+) create mode 100644 fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/option/JdkVariantOptions.java create mode 100644 fixture-monkey-api/src/main/java17/com/navercorp/fixturemonkey/api/generator/SealedTypeObjectPropertyGenerator.java create mode 100644 fixture-monkey-api/src/main/java17/com/navercorp/fixturemonkey/api/option/JdkVariantOptions.java create mode 100644 fixture-monkey-tests/java-17-tests/src/test/java/com/navercorp/fixturemonkey/tests/java17/SealedClassTest.java diff --git a/docs/content/v1.0.x/release-notes/_index.md b/docs/content/v1.0.x/release-notes/_index.md index 712779213..6be39a61b 100644 --- a/docs/content/v1.0.x/release-notes/_index.md +++ b/docs/content/v1.0.x/release-notes/_index.md @@ -5,6 +5,12 @@ menu: docs: weight: 100 --- +sectionStart +### v.1.0.14 +Add supporting for sealed class and sealed interface. + +sectionEnd + sectionStart ### v.1.0.13 Add InterfacePlugin supports abstract classes through `abstractClassExtends` option. diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/option/FixtureMonkeyOptionsBuilder.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/option/FixtureMonkeyOptionsBuilder.java index aaf1a5c6d..ddd229ce5 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/option/FixtureMonkeyOptionsBuilder.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/option/FixtureMonkeyOptionsBuilder.java @@ -133,6 +133,7 @@ public final class FixtureMonkeyOptionsBuilder { @Nullable private Function generateJavaTimeArbitrarySet = null; private InstantiatorProcessor instantiatorProcessor = new JavaInstantiatorProcessor(); + private final JdkVariantOptions jdkVariantOptions = new JdkVariantOptions(); FixtureMonkeyOptionsBuilder() { } @@ -511,6 +512,8 @@ public FixtureMonkeyOptionsBuilder instantiatorProcessor( } public FixtureMonkeyOptions build() { + jdkVariantOptions.apply(this); + ObjectPropertyGenerator defaultObjectPropertyGenerator = defaultIfNull( this.defaultObjectPropertyGenerator, () -> FixtureMonkeyOptions.DEFAULT_OBJECT_PROPERTY_GENERATOR diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/option/JdkVariantOptions.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/option/JdkVariantOptions.java new file mode 100644 index 000000000..f173b752c --- /dev/null +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/option/JdkVariantOptions.java @@ -0,0 +1,29 @@ +/* + * Fixture Monkey + * + * Copyright (c) 2021-present NAVER Corp. + * + * Licensed 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 com.navercorp.fixturemonkey.api.option; + +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; + +@API(since = "1.0.14", status = Status.INTERNAL) +public final class JdkVariantOptions { + public void apply(FixtureMonkeyOptionsBuilder optionsBuilder){ + // omitted because JDK8 is a default version + } +} diff --git a/fixture-monkey-api/src/main/java17/com/navercorp/fixturemonkey/api/generator/SealedTypeObjectPropertyGenerator.java b/fixture-monkey-api/src/main/java17/com/navercorp/fixturemonkey/api/generator/SealedTypeObjectPropertyGenerator.java new file mode 100644 index 000000000..d3a86f31b --- /dev/null +++ b/fixture-monkey-api/src/main/java17/com/navercorp/fixturemonkey/api/generator/SealedTypeObjectPropertyGenerator.java @@ -0,0 +1,58 @@ +/* + * Fixture Monkey + * + * Copyright (c) 2021-present NAVER Corp. + * + * Licensed 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 com.navercorp.fixturemonkey.api.generator; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; + +import com.navercorp.fixturemonkey.api.property.Property; +import com.navercorp.fixturemonkey.api.property.PropertyUtils; +import com.navercorp.fixturemonkey.api.type.Types; + +@API(since = "1.0.14", status = Status.EXPERIMENTAL) +public final class SealedTypeObjectPropertyGenerator implements ObjectPropertyGenerator { + @Override + public ObjectProperty generate(ObjectPropertyGeneratorContext context) { + Property sealedTypeProperty = context.getProperty(); + Class actualType = Types.getActualType(sealedTypeProperty.getType()); + double nullInject = context.getNullInjectGenerator().generate(context); + + Map> childPropertiesByProperty = new HashMap<>(); + for (Class subClass : actualType.getPermittedSubclasses()) { + Property subProperty = PropertyUtils.toProperty(subClass); + + List subPropertyChildProperties = context.getPropertyGenerator() + .generateChildProperties(subProperty); + + childPropertiesByProperty.put(subProperty, subPropertyChildProperties); + } + + return new ObjectProperty( + sealedTypeProperty, + context.getPropertyNameResolver(), + nullInject, + context.getElementIndex(), + childPropertiesByProperty + ); + } +} diff --git a/fixture-monkey-api/src/main/java17/com/navercorp/fixturemonkey/api/option/JdkVariantOptions.java b/fixture-monkey-api/src/main/java17/com/navercorp/fixturemonkey/api/option/JdkVariantOptions.java new file mode 100644 index 000000000..dbafab791 --- /dev/null +++ b/fixture-monkey-api/src/main/java17/com/navercorp/fixturemonkey/api/option/JdkVariantOptions.java @@ -0,0 +1,39 @@ +/* + * Fixture Monkey + * + * Copyright (c) 2021-present NAVER Corp. + * + * Licensed 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 com.navercorp.fixturemonkey.api.option; + +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; + +import com.navercorp.fixturemonkey.api.generator.ObjectPropertyGenerator; +import com.navercorp.fixturemonkey.api.generator.SealedTypeObjectPropertyGenerator; +import com.navercorp.fixturemonkey.api.type.Types; + +@API(since = "1.0.14", status = Status.INTERNAL) +public final class JdkVariantOptions { + private static final ObjectPropertyGenerator SEALED_TYPE_OBJECT_PROPERTY_GENERATOR = + new SealedTypeObjectPropertyGenerator(); + + public void apply(FixtureMonkeyOptionsBuilder optionsBuilder) { + optionsBuilder.insertFirstArbitraryObjectPropertyGenerator( + p -> Types.getActualType(p.getType()).isSealed(), + SEALED_TYPE_OBJECT_PROPERTY_GENERATOR + ); + } +} diff --git a/fixture-monkey-tests/java-17-tests/src/test/java/com/navercorp/fixturemonkey/tests/java17/SealedClassTest.java b/fixture-monkey-tests/java-17-tests/src/test/java/com/navercorp/fixturemonkey/tests/java17/SealedClassTest.java new file mode 100644 index 000000000..5b49fb72f --- /dev/null +++ b/fixture-monkey-tests/java-17-tests/src/test/java/com/navercorp/fixturemonkey/tests/java17/SealedClassTest.java @@ -0,0 +1,100 @@ +/* + * Fixture Monkey + * + * Copyright (c) 2021-present NAVER Corp. + * + * Licensed 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 com.navercorp.fixturemonkey.tests.java17; + +import static com.navercorp.fixturemonkey.tests.TestEnvironment.TEST_COUNT; +import static org.assertj.core.api.BDDAssertions.then; + +import java.beans.ConstructorProperties; + +import org.junit.jupiter.api.RepeatedTest; + +import com.navercorp.fixturemonkey.FixtureMonkey; +import com.navercorp.fixturemonkey.api.introspector.ConstructorPropertiesArbitraryIntrospector; + +class SealedClassTest { + private static final FixtureMonkey SUT = FixtureMonkey.builder() + .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) + .defaultNotNull(true) + .build(); + + private static sealed class SealedClass permits SealedClassImpl { + } + + private static final class SealedClassImpl extends SealedClass { + private final String value; + + @ConstructorProperties("value") + public SealedClassImpl(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + @RepeatedTest(TEST_COUNT) + void sampleSealedClass() { + // when + SealedClass actual = SUT.giveMeOne(SealedClass.class); + + then(actual).isInstanceOf(SealedClassImpl.class); + } + + private sealed interface SealedInterface permits SealedInterfaceImpl { + } + + private record SealedInterfaceImpl(String value) implements SealedInterface { + } + + @RepeatedTest(TEST_COUNT) + void sampleSealedInterface() { + // when + SealedInterface actual = SUT.giveMeOne(SealedInterface.class); + + then(actual).isInstanceOf(SealedInterface.class); + } + + private record SealedClassProperty( + SealedClass sealedClass, + SealedInterface sealedInterface + ) { + } + + @RepeatedTest(TEST_COUNT) + void sampleSealedClassProperty() { + // when + SealedClassProperty actual = SUT.giveMeOne(SealedClassProperty.class); + + then(actual.sealedInterface()).isInstanceOf(SealedInterfaceImpl.class); + then(actual.sealedClass()).isInstanceOf(SealedClassImpl.class); + } + + @RepeatedTest(TEST_COUNT) + void fixedSealedClassProperty() { + // when + SealedClassProperty actual = SUT.giveMeBuilder(SealedClassProperty.class) + .fixed() + .sample(); + + then(actual.sealedInterface()).isInstanceOf(SealedInterfaceImpl.class); + then(actual.sealedClass()).isInstanceOf(SealedClassImpl.class); + } +}