Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fix: collection/map spec with subtype throws exception from GeneratorProvider SPI #1030

Merged
merged 1 commit into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.instancio.Random;
import org.instancio.exception.InstancioException;
import org.instancio.generator.GeneratorContext;
import org.instancio.internal.util.ReflectionUtils;

import java.util.Collection;

Expand All @@ -32,7 +33,11 @@ public CollectionGeneratorSpecImpl(final GeneratorContext context) {
}

@Override
@SuppressWarnings("unchecked")
protected Collection<T> tryGenerateNonNull(final Random random) {
if (collectionType != null) {
return (Collection<T>) ReflectionUtils.newInstance(collectionType);
}
throw new InstancioException(getClass() + " should delegate to another generator");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.instancio.Random;
import org.instancio.exception.InstancioException;
import org.instancio.generator.GeneratorContext;
import org.instancio.internal.util.ReflectionUtils;

import java.util.Map;

Expand All @@ -32,7 +33,11 @@ public MapGeneratorSpecImpl(final GeneratorContext context) {
}

@Override
@SuppressWarnings("unchecked")
public Map<K, V> tryGenerateNonNull(final Random random) {
if (mapType != null) {
return (Map<K, V>) ReflectionUtils.newInstance(mapType);
}
throw new InstancioException(getClass() + " should delegate to another generator");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.slf4j.LoggerFactory;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
Expand Down Expand Up @@ -177,5 +178,14 @@ public static Class<?> getClass(final String name) {
throw Fail.withUsageError("failed loading class: '%s'", name, ex);
}
}

public static <T> T newInstance(final Class<T> klass) {
try {
final Constructor<T> ctor = klass.getDeclaredConstructor();
return ctor.newInstance();
} catch (Exception ex) {
throw new InstancioException("Error instantiating " + klass, ex);
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2022-2024 the original author or authors.
*
* 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
*
* https://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.instancio.internal.generator.util;

import org.instancio.Random;
import org.instancio.exception.InstancioException;
import org.instancio.generator.GeneratorContext;
import org.instancio.settings.Settings;
import org.instancio.support.DefaultRandom;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class CollectionGeneratorSpecImplTest {

private final Random random = new DefaultRandom();
private final GeneratorContext context = new GeneratorContext(Settings.defaults(), random);

private final CollectionGeneratorSpecImpl<?> generator = new CollectionGeneratorSpecImpl<>(context);

@Test
void shouldFailIfNoSubtypeIsProvided() {
assertThatThrownBy(() -> generator.generate(random))
.isExactlyInstanceOf(InstancioException.class)
.hasMessage("%s should delegate to another generator", generator.getClass());
}

@Test
void shouldCreateANewInstanceIfSubtypeIsProvided() {
generator.subtype(ArrayList.class);

assertThat(generator.generate(random)).isInstanceOf(ArrayList.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2022-2024 the original author or authors.
*
* 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
*
* https://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.instancio.internal.generator.util;

import org.instancio.Random;
import org.instancio.exception.InstancioException;
import org.instancio.generator.GeneratorContext;
import org.instancio.settings.Settings;
import org.instancio.support.DefaultRandom;
import org.junit.jupiter.api.Test;

import java.util.TreeMap;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class MapGeneratorSpecImplTest {

private final Random random = new DefaultRandom();
private final GeneratorContext context = new GeneratorContext(Settings.defaults(), random);

private final MapGeneratorSpecImpl<?, ?> generator = new MapGeneratorSpecImpl<>(context);

@Test
void shouldFailIfNoSubtypeIsProvided() {
assertThatThrownBy(() -> generator.generate(random))
.isExactlyInstanceOf(InstancioException.class)
.hasMessage("%s should delegate to another generator", generator.getClass());
}

@Test
void shouldCreateANewInstanceIfSubtypeIsProvided() {
generator.subtype(TreeMap.class);

assertThat(generator.generate(random)).isInstanceOf(TreeMap.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import org.instancio.spi.InstancioServiceProvider;
import org.instancio.spi.ServiceProviderContext;
import org.instancio.test.support.pojo.arrays.object.WithIntegerArray;
import org.instancio.test.support.pojo.collections.lists.ListInteger;
import org.instancio.test.support.pojo.collections.maps.MapIntegerItemOfString;
import org.instancio.test.support.pojo.collections.maps.MapIntegerString;
import org.instancio.test.support.pojo.collections.sets.SetInteger;
import org.instancio.test.support.pojo.dynamic.DynPhone;
Expand All @@ -43,8 +45,11 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -116,6 +121,14 @@ private static class GeneratorProviderImpl implements GeneratorProvider {
public GeneratorSpec<?> getGenerator(final Node node, final Generators generators) {
if (node.getField() != null) {

// Ensure collection/map generators work with subtype()
if (node.getParent().getTargetClass() == ListInteger.class && node.getField().getType() == List.class) {
return generators.collection().subtype(LinkedList.class);
}
if (node.getParent().getTargetClass() == MapIntegerItemOfString.class && node.getField().getType() == Map.class) {
return generators.map().subtype(TreeMap.class);
}

if (node.getParent().getTargetClass() == SetInteger.class && node.getField().getType() == Set.class) {
return generators.collection().size(10);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import org.instancio.spi.InstancioSpiException;
import org.instancio.test.support.pojo.arrays.object.WithIntegerArray;
import org.instancio.test.support.pojo.basic.IntegerHolder;
import org.instancio.test.support.pojo.collections.lists.ListInteger;
import org.instancio.test.support.pojo.collections.maps.MapIntegerItemOfString;
import org.instancio.test.support.pojo.collections.maps.MapIntegerString;
import org.instancio.test.support.pojo.collections.sets.SetInteger;
import org.instancio.test.support.pojo.dynamic.DynPhone;
Expand All @@ -52,8 +54,10 @@
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -334,6 +338,23 @@ void mapSpecWithImmutableMap() {
.hasSize(10);
}

@Nested
class SpecsWithSubtypeTest {
@Test
void collectionSpecWithSubtype() {
final ListInteger result = Instancio.create(ListInteger.class);

assertThat(result.getList()).isExactlyInstanceOf(LinkedList.class);
}

@Test
void mapSpecWithSubtype() {
final MapIntegerItemOfString result = Instancio.create(MapIntegerItemOfString.class);

assertThat(result.getMap()).isExactlyInstanceOf(TreeMap.class);
}
}

/**
* Generator provider returning {@link Generators#array()}.
*/
Expand Down