diff --git a/src/test/java/net/datafaker/providers/base/CustomFakerTest.java b/src/test/java/net/datafaker/providers/base/CustomFakerTest.java index e2f4b76fe..7f1abaccf 100644 --- a/src/test/java/net/datafaker/providers/base/CustomFakerTest.java +++ b/src/test/java/net/datafaker/providers/base/CustomFakerTest.java @@ -1,5 +1,6 @@ package net.datafaker.providers.base; +import net.datafaker.annotations.Deterministic; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; @@ -155,4 +156,103 @@ void weightedInsectNameTest() { assertThat(insectCounts.get("Driver ant")).as(() -> "Insects: " + insectCounts).isGreaterThan(insectCounts.get("Fire ant")); assertThat(insectCounts.get("Fire ant")).as(() -> "Insects: " + insectCounts).isGreaterThan(insectCounts.get("Harvester ant")); } + + public static final class HolderA { + public static final class WidgetCustomTestFaker extends AbstractProvider { + public WidgetCustomTestFaker(BaseProviders faker) { + super(faker); + } + + @Deterministic + public String value() { + return "A-widget"; + } + } + } + + public static final class HolderB { + public static final class WidgetCustomTestFaker extends AbstractProvider { + public WidgetCustomTestFaker(BaseProviders faker) { + super(faker); + } + + @Deterministic + public String value() { + return "B-widget"; + } + } + } + + public static final class FakerA extends BaseFaker { + public FakerA() { + super(Locale.ENGLISH); + } + + public HolderA.WidgetCustomTestFaker widget() { + return getProvider(HolderA.WidgetCustomTestFaker.class, HolderA.WidgetCustomTestFaker::new); + } + } + + public static final class FakerB extends BaseFaker { + public FakerB() { + super(Locale.ENGLISH); + } + + public HolderB.WidgetCustomTestFaker widget() { + return getProvider(HolderB.WidgetCustomTestFaker.class, HolderB.WidgetCustomTestFaker::new); + } + } + + public static final class HolderG { + public static final class GizmoCustomTestFaker extends AbstractProvider { + public GizmoCustomTestFaker(BaseProviders faker) { + super(faker); + } + + @Deterministic + public String value() { + return "G-value"; + } + } + } + + public static final class FakerG extends BaseFaker { + public FakerG() { + super(Locale.ENGLISH); + } + + public HolderG.GizmoCustomTestFaker gizmo() { + return getProvider(HolderG.GizmoCustomTestFaker.class, HolderG.GizmoCustomTestFaker::new); + } + } + + @Test + void recipeMustNotLeakAcrossRootsWithSameSimpleProviderName() { + // Need for multiple iteration because of possible cache issues + for (int i = 0; i < 100; i++) { + FakerA a = new FakerA(); + FakerB b = new FakerB(); + + // Sanity: each faker resolves its own provider correctly in isolation. + assertThat(a.expression("#{Widget.value}")).isEqualTo("A-widget"); // populates static recipe + + // HolderA.Widget#value against a HolderB.Widget instance. + assertThat(b.expression("#{Widget.value}")).isEqualTo("B-widget"); + } + } + + @Test + void recipeMustNotNpeWhenOtherRootLacksTheProvider() { + // Need for multiple iteration because of possible cache issues + for (int i = 0; i < 100; i++) { + FakerG withGizmo = new FakerG(); + BaseFaker plain = new BaseFaker(Locale.ENGLISH); + + assertThat(withGizmo.expression("#{Gizmo.value}")).isEqualTo("G-value"); // populates static recipe + + assertThatThrownBy(() -> plain.expression("#{Gizmo.value}")) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Unable to resolve"); + } + } } diff --git a/src/test/java/net/datafaker/script/ProvidersDocsGenerator.java b/src/test/java/net/datafaker/script/ProvidersDocsGenerator.java index 15933db17..c50034e4a 100644 --- a/src/test/java/net/datafaker/script/ProvidersDocsGenerator.java +++ b/src/test/java/net/datafaker/script/ProvidersDocsGenerator.java @@ -40,7 +40,8 @@ public class ProvidersDocsGenerator { private final Set> subTypes = new TreeSet<>(providersComparatorBySimpleName); // Exclude non-providers from generation - private static final Set providersToExcludeFromGeneration = Set.of("CustomFakerTest", "InsectFromFile", "Insect"); + private static final Set providersToExcludeFromGeneration = + Set.of("CustomFakerTest", "InsectFromFile", "Insect", "GizmoCustomTestFaker", "WidgetCustomTestFaker"); private final Set fakersWithoutSinceTag = new HashSet<>(); @@ -74,7 +75,7 @@ void generateProvidersDocs(BufferedWriter writer) throws IOException { System.out.println("Providers without '@since' tag: " + fakersWithoutSinceTag); } - private String extractGroupName(Class clazz) { + private static String extractGroupName(Class clazz) { // `packageName` should be such format: net.datafaker.providers. (e.g. base, sport, movie) String packageName = clazz.getPackage().getName(); // And just splitting by '.' we're getting groupName (e.g. base, sport, movie) @@ -143,7 +144,7 @@ private String providersPerVersionTable() { Map providersPerVersion = extractProvidersPerVersion(); StringBuilder sb = new StringBuilder() - .append("\nNumber of providers per Datafaker version\n") + .append("\nNumber of providers per Datafaker version:\n") .append("\n| Version | Number of new providers | Total number of providers |") .append("\n|---------|-------------------------|---------------------------|\n"); @@ -180,7 +181,7 @@ private Map extractProvidersPerVersion() { return providersPerVersion; } - private String formatGroupName(String groupName) { + private static String formatGroupName(String groupName) { return Character.toUpperCase(groupName.charAt(0)) + groupName.substring(1); }