Skip to content

Add StringCombinableArbitrary for easy efficient customisation #1179

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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 @@

package com.navercorp.fixturemonkey.api.arbitrary;

import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
Expand All @@ -37,7 +38,10 @@
@API(since = "0.6.0", status = Status.MAINTAINED)
public interface CombinableArbitrary<T> {
CombinableArbitrary<?> NOT_GENERATED = CombinableArbitrary.from((Object)null);

int DEFAULT_MAX_TRIES = 1_000;
ServiceLoader<StringCombinableArbitrary> STRING_COMBINABLE_ARBITRARY_SERVICE_LOADER =
ServiceLoader.load(StringCombinableArbitrary.class);

/**
* Generates a {@link FixedCombinableArbitrary} which returns always same value.
Expand Down Expand Up @@ -186,4 +190,15 @@ default CombinableArbitrary<T> unique() {
* @return fixed
*/
boolean fixed();

/**
* Generates a {@link StringCombinableArbitrary} which returns a randomly generated String.
* You can customize the generated String by using {@link StringCombinableArbitrary}.
*
* @return a {@link CombinableArbitrary} returns a randomly generated String
*/
@API(since = "1.1.12", status = Status.EXPERIMENTAL)
static StringCombinableArbitrary strings() {
return STRING_COMBINABLE_ARBITRARY_SERVICE_LOADER.iterator().next();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.arbitrary;

import java.util.function.Predicate;

public interface StringCombinableArbitrary extends CombinableArbitrary<String> {
int STRING_DEFAULT_MIN_LENGTH = 0;
int STRING_DEFAULT_MAX_LENGTH = 100;

@Override
String combined();

@Override
String rawValue();

StringCombinableArbitrary withLength(int min, int max);

/**
* Generates a StringCombinableArbitrary which contains only alphabetic characters.
* It conflicts with {@link #ascii()} and {@link #numeric()} and {@link #korean()}.
*
* @return the StringCombinableArbitrary which contains only alphabetic characters
*/
StringCombinableArbitrary alphabetic();

/**
* Generates a StringCombinableArbitrary which contains only ASCII characters.
* It conflicts with {@link #alphabetic()} and {@link #numeric()} and {@link #korean()}.
*
* @return the StringCombinableArbitrary which contains only ASCII characters
*/
StringCombinableArbitrary ascii();

/**
* Generates a StringCombinableArbitrary which contains only numeric characters.
* It conflicts with {@link #alphabetic()} and {@link #ascii()} and {@link #korean()}.
*
* @return the StringCombinableArbitrary which contains only numeric characters
*/
StringCombinableArbitrary numeric();

/**
* Generates a StringCombinableArbitrary which contains only Korean characters.
* It conflicts with {@link #alphabetic()} and {@link #ascii()} and {@link #numeric()}.
*
* @return the StringCombinableArbitrary which contains only Korean characters
*/
StringCombinableArbitrary korean();

default StringCombinableArbitrary withMinLength(int min) {
return this.withLength(min, STRING_DEFAULT_MAX_LENGTH);
}

default StringCombinableArbitrary withMaxLength(int max) {
return this.withLength(STRING_DEFAULT_MIN_LENGTH, max);
}

@Override
default StringCombinableArbitrary filter(Predicate<String> predicate) {
return this.filter(DEFAULT_MAX_TRIES, predicate);
}

@Override
default StringCombinableArbitrary filter(int tries, Predicate<String> predicate) {
return new StringCombinableArbitraryDelegator(CombinableArbitrary.super.filter(tries, predicate));
}

default StringCombinableArbitrary filterCharacter(Predicate<Character> predicate) {
return this.filterCharacter(DEFAULT_MAX_TRIES, predicate);
}

StringCombinableArbitrary filterCharacter(int tries, Predicate<Character> predicate);

@Override
default StringCombinableArbitrary injectNull(double nullProbability) {
return new StringCombinableArbitraryDelegator(CombinableArbitrary.super.injectNull(nullProbability));
}

@Override
default StringCombinableArbitrary unique() {
return new StringCombinableArbitraryDelegator(CombinableArbitrary.super.unique());
}

@Override
void clear();

@Override
boolean fixed();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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.arbitrary;

import java.util.function.Predicate;

final class StringCombinableArbitraryDelegator implements StringCombinableArbitrary {
private final CombinableArbitrary<String> delegate;

public StringCombinableArbitraryDelegator(CombinableArbitrary<String> delegate) {
this.delegate = delegate;
}

@Override
public String combined() {
return delegate.combined();
}

@Override
public String rawValue() {
return delegate.combined();
}

@Override
public StringCombinableArbitrary withLength(int min, int max) {
return new StringCombinableArbitraryDelegator(delegate.filter(it -> min <= it.length() && it.length() <= max));
}

@Override
public StringCombinableArbitrary alphabetic() {
return CombinableArbitrary.strings().alphabetic();
}

@Override
public StringCombinableArbitrary ascii() {
return CombinableArbitrary.strings().ascii();
}

@Override
public StringCombinableArbitrary numeric() {
return CombinableArbitrary.strings().numeric();
}

@Override
public StringCombinableArbitrary korean() {
return CombinableArbitrary.strings().korean();
}

@Override
public StringCombinableArbitrary filter(int tries, Predicate<String> predicate) {
return new StringCombinableArbitraryDelegator(delegate.filter(tries, predicate));
}

@Override
public StringCombinableArbitrary filterCharacter(int tries, Predicate<Character> predicate) {
return this.filter(tries, it -> it.chars().mapToObj(Character.class::cast).allMatch(predicate));
}

@Override
public StringCombinableArbitrary unique() {
return new StringCombinableArbitraryDelegator(delegate.unique());
}

@Override
public void clear() {
delegate.clear();
}

@Override
public boolean fixed() {
return delegate.fixed();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ public JqwikJavaTypeArbitraryGeneratorSet(

@Override
public CombinableArbitrary<String> strings(ArbitraryGeneratorContext context) {
return ArbitraryUtils.toCombinableArbitrary(arbitraryResolver.strings(arbitraryGenerator.strings(), context));
return ArbitraryUtils.toCombinableArbitrary(
arbitraryResolver.strings(arbitraryGenerator.strings(), context)
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* 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.jqwik;

import java.util.List;
import java.util.function.Predicate;

import javax.annotation.Nullable;

import net.jqwik.api.Arbitraries;
import net.jqwik.api.Arbitrary;
import net.jqwik.api.arbitraries.ListArbitrary;

import com.navercorp.fixturemonkey.api.arbitrary.StringCombinableArbitrary;

public final class JqwikStringCombinableArbitrary implements StringCombinableArbitrary {
private final Arbitrary<Character> characterArbitrary;
@Nullable
private Integer minSize = null;
@Nullable
private Integer maxSize = null;

public JqwikStringCombinableArbitrary() {
this.characterArbitrary = Arbitraries.chars();
}

private JqwikStringCombinableArbitrary(Arbitrary<Character> characterArbitrary) {
this.characterArbitrary = characterArbitrary;
}

@Override
public String combined() {
ListArbitrary<Character> characterListArbitrary = characterArbitrary.list();
if (this.minSize != null) {
characterListArbitrary = characterListArbitrary.ofMinSize(this.minSize);
}

if (this.maxSize != null) {
characterListArbitrary = characterListArbitrary.ofMaxSize(this.maxSize);
}

List<Character> characters = characterListArbitrary.sample();
StringBuilder stringBuilder = new StringBuilder();
for (Character character : characters) {
stringBuilder.append(character);
}
return stringBuilder.toString();
}

@Override
public String rawValue() {
return this.combined();
}

@Override
public StringCombinableArbitrary withLength(int minSize, int maxSize) {
this.minSize = minSize;
this.maxSize = maxSize;
return this;
}

@Override
public StringCombinableArbitrary alphabetic() {
return new JqwikStringCombinableArbitrary(Arbitraries.chars().alpha());
}

@Override
public StringCombinableArbitrary ascii() {
return new JqwikStringCombinableArbitrary(Arbitraries.chars().ascii());
}

@Override
public StringCombinableArbitrary numeric() {
return new JqwikStringCombinableArbitrary(Arbitraries.chars().numeric());
}

@Override
public StringCombinableArbitrary korean() {
return new JqwikStringCombinableArbitrary(Arbitraries.chars().filter(
it -> '가' <= it && it <= '힣'
));
}

@Override
public StringCombinableArbitrary filterCharacter(int tries, Predicate<Character> predicate) {
return new JqwikStringCombinableArbitrary(this.characterArbitrary.filter(tries, predicate));
}

@Override
public void clear() {
// ignored
}

@Override
public boolean fixed() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.navercorp.fixturemonkey.api.jqwik.JqwikStringCombinableArbitrary
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems it's already loaded (it's inside the api module). Is that ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I test it, SPI does not load the implementation implicitly.
It should specify the implementation in META-INF/services.

I also did not find the following specification in the document.

https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html

image

Loading
Loading