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

Issue-#194: Correct logic to identify 'mandatory' parameters #199

Draft
wants to merge 2 commits into
base: 0.10.x-develop
Choose a base branch
from
Draft
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 @@ -5,15 +5,13 @@
import com.github.jcunit.model.ValueResolver;
import com.github.jcunit.factorspace.Parameter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.*;
import java.util.function.Supplier;

import static com.github.jcunit.model.ParameterSpec.Utils.createConstraints;
import static com.github.jcunit.model.ParameterSpec.Utils.isRequired;
import static java.lang.annotation.ElementType.METHOD;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
Expand All @@ -32,15 +30,14 @@ enum Type {
};


public static <T> Parameter<List<ValueResolver<T>>> createListSimple(ParameterSpec<T> parameterSpec, ParameterSpaceSpec parameterSpaceSpec) {
boolean isSeed = isRequired(parameterSpaceSpec, parameterSpec.name(), parameterSpaceSpec.parameterNames());
return new Parameter.Simple.Impl<>(!isSeed,
public static <T> Parameter<List<ValueResolver<T>>> createListSimple(ParameterSpec<T> parameterSpec, ParameterSpaceSpec parameterSpaceSpec, boolean optional) {
return new Parameter.Simple.Impl<>(optional,
parameterSpec.name(),
parameterSpec.valueResolvers()
.stream()
.map(Collections::singletonList)
.collect(toList()),
createConstraints(isSeed,
createConstraints(!optional,
parameterSpaceSpec,
parameterSpec.name()));
}
Expand Down
59 changes: 28 additions & 31 deletions src/main/java/com/github/jcunit/factorspace/ParameterSpace.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,84 +8,81 @@
import java.util.List;

import static java.lang.String.format;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.toList;

public interface ParameterSpace {
List<String> getParameterNames();

<P> Parameter<P> getParameter(String name);

List<Constraint> getConstraints();

default Tuple encodeTuple(Tuple tuple) {
Tuple.Builder builder = Tuple.builder();
this.getParameterNames()
.forEach(
each -> this.getParameter(each)
.decomposeValue(tuple.get(each))
.ifPresent(builder::putAll));
.decomposeValue(tuple.get(each))
.ifPresent(builder::putAll));
return builder.build();
}

static List<Tuple> encodeSeedTuples(ParameterSpace parameterSpace, List<Tuple> seeds) {
return seeds.stream()
.map(parameterSpace::encodeTuple)
.collect(toList());
.map(parameterSpace::encodeTuple)
.collect(toList());
}

class Builder {
List<Parameter<?>> parameters = new LinkedList<>();
List<Constraint> constraints = new LinkedList<>();

public Builder addParameter(Parameter<?> parameter) {
this.parameters.add(parameter);
return this;
}

public Builder addAllParameters(Collection<? extends Parameter<?>> parameters) {
parameters.forEach(Builder.this::addParameter);
return this;
}

public Builder addConstraint(Constraint constraint) {
this.constraints.add(constraint);
return this;
}

public Builder addAllConstraints(Collection<? extends Constraint> constraints) {
constraints.forEach(Builder.this::addConstraint);
return this;
}

public ParameterSpace build() {
return new ParameterSpace() {
@Override
public List<String> getParameterNames() {
return parameters.stream().map(Parameter::getName).collect(toList());
return parameters.stream()
.map(Parameter::getName)
.collect(toList());
}

@SuppressWarnings("unchecked")
@Override
public <P> Parameter<P> getParameter(String name) {
return (Parameter<P>) (parameters.stream(

).filter(
parameter -> parameter.getName().equals(name)
).findFirst(
).orElseThrow(
() -> new RuntimeException(format(
"Parameter '%s' was requested but not found. Existing parameters are %s",
name,
getParameterNames()
))
));
return (Parameter<P>) (parameters.stream()
.filter(parameter -> parameter.getName().equals(name))
.findFirst()
.orElseThrow(() -> new RuntimeException(format("Parameter '%s' was requested but not found. Existing parameters are %s",
name,
getParameterNames()))));
}

@Override
public List<Constraint> getConstraints() {
return Collections.unmodifiableList(constraints);
return unmodifiableList(constraints);
}

@Override
public String toString() {
return format("parameters:%s,constraints:%s", parameters, constraints);
Expand Down
93 changes: 91 additions & 2 deletions src/main/java/com/github/jcunit/model/ParameterSpaceSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.function.Function;

import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;

/**
* // @formatter:off
Expand Down Expand Up @@ -59,15 +60,103 @@ public <E> ParameterSpec<E> parameterSpecFor(String parameterName) {

<E> ParameterSpec<E> parameterSpecFor(String parameterName);

default ParameterSpace toParameterSpace() {
default ParameterSpace toParameterSpace(List<String> seedParameterNames) {
Set<String> optionals = Utils.optionalParameterNamesIn(this.parameterNames()
.stream()
.map(this::parameterSpecFor)
.collect(toSet()),
seedParameterNames);
ParameterSpace.Builder b = new ParameterSpace.Builder();
this.parameterNames()
.stream()
.map(this::parameterSpecFor)
.map(each -> each.toParameter(this))
.map(each -> each.toParameter(this, optionals.contains(each.name())))
.forEach(b::addParameter);
this.constraints()
.forEach(b::addConstraint);
return b.build();
}

enum Utils {
;

private static Set<String> optionalParameterNamesIn(Collection<ParameterSpec<?>> allParameterSpecs, Collection<String> seedParameterNames) {
return optionalParameterNamesIn(allParameterSpecs.stream()
.collect(toMap(ParameterSpec::name, e -> e)), seedParameterNames
);
}

private static Set<String> optionalParameterNamesIn(Map<String, ParameterSpec<?>> allParameterSpecs, Collection<String> seedParameterNames) {
Set<String> ret = reachableParameterNamesIn(allParameterSpecs, seedParameterNames);
ret.removeAll(requiredParameterNamesIn(allParameterSpecs, seedParameterNames));
return ret;
}

private static Set<String> reachableParameterNamesIn(Map<String, ParameterSpec<?>> allParameterSpecs, Collection<String> seedParameterNames) {
return seedParameterNames.stream()
.flatMap(n -> reachableParameterNamesFrom(allParameterSpecs.get(n),
new HashSet<>(allParameterSpecs.values())).stream())
.collect(toSet());
}

private static Set<String> requiredParameterNamesIn(Map<String, ParameterSpec<?>> allParameterSpecs, Collection<String> seedParameterNames) {
return seedParameterNames.stream()
.flatMap(n -> requiredParameterNamesBy(allParameterSpecs.get(n),
new HashSet<>(allParameterSpecs.values())).stream())
.collect(toSet());
}

private static Set<String> reachableParameterNamesFrom(ParameterSpec<?> cur, Set<ParameterSpec<?>> allParameterSpecs) {
Set<String> ret = new HashSet<>();
Set<ParameterSpec<?>> visited = new HashSet<>();
reachableParameterNamesFrom(ret, cur, visited, allParameterSpecs);
return ret;
}

private static void reachableParameterNamesFrom(Set<String> out, ParameterSpec<?> cur, Set<ParameterSpec<?>> visited, Set<ParameterSpec<?>> allParameterSpecs) {
if (visited.contains(cur)) {
return;
}
visited.add(cur);
out.addAll(directlyReachableParameterNamesFrom(cur));
allParameterSpecs.forEach(p -> reachableParameterNamesFrom(out, p, visited, allParameterSpecs));
}

private static Set<String> directlyReachableParameterNamesFrom(ParameterSpec<?> parameterSpec) {
return parameterSpec.valueResolvers()
.stream()
.flatMap(r -> r.dependencies().stream())
.collect(toSet());
}

private static Set<String> requiredParameterNamesBy(ParameterSpec<?> cur, Set<ParameterSpec<?>> allParameterSpecs) {
Set<String> ret = new HashSet<>();
Set<ParameterSpec<?>> visited = new HashSet<>();
requiredParameterNamesBy(ret, cur, visited, allParameterSpecs);
return ret;
}


private static void requiredParameterNamesBy(Set<String> out, ParameterSpec<?> cur, Set<ParameterSpec<?>> visited, Set<ParameterSpec<?>> allParameterSpecs) {
if (visited.contains(cur)) {
return;
}
visited.add(cur);
out.addAll(directlyRequiredParameterNamesBy(cur));
allParameterSpecs.forEach(p -> reachableParameterNamesFrom(out, p, visited, allParameterSpecs));
}

static Set<String> directlyRequiredParameterNamesBy(ParameterSpec<?> parameterSpec) {
Set<String> requiredParameterNames = null;
for (ValueResolver<?> r : parameterSpec.valueResolvers()) {
if (requiredParameterNames == null) requiredParameterNames = new HashSet<>(r.dependencies());
else requiredParameterNames.retainAll(r.dependencies());
if (requiredParameterNames.isEmpty()) {
break;
}
}
return requiredParameterNames == null ? new HashSet<>()
: requiredParameterNames;
}
}
}
4 changes: 2 additions & 2 deletions src/main/java/com/github/jcunit/model/ParameterSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ default List<String> dependencies() {
*/
List<ValueResolver<E>> valueResolvers();

default Parameter<List<ValueResolver<E>>> toParameter(ParameterSpaceSpec parameterSpaceSpec) {
default Parameter<List<ValueResolver<E>>> toParameter(ParameterSpaceSpec parameterSpaceSpec, boolean optional) {
switch (type()) {
case SIMPLE:
return Type.createListSimple(this, parameterSpaceSpec);
return Type.createListSimple(this, parameterSpaceSpec, optional);
case REGEX:
boolean isSeed = Utils.isRequired(parameterSpaceSpec, this.name(), parameterSpaceSpec.parameterNames());
return Type.createRegex(isSeed, this);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/github/jcunit/pipeline/Pipeline.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public interface Pipeline {
@ParseConfigArgumentsWith(Standard.ConfigArgumentsParser.class)
class Standard implements Pipeline {
public static class ConfigArgumentsParser implements ConfigurePipelineWith.PipelineConfigArgumentsParser {
enum Keyword {
public enum Keyword {
STRENGTH("strength"),
NEGATIVE_TEST_GENERATION("negativeTestGeneration"),
SEED_GENERATOR_METHOD("seedGeneratorMethod");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import static com.github.jcunit.runners.junit5.JCUnitTestEngineUtils.*;
import static com.github.valid8j.fluent.Expectations.require;
import static com.github.valid8j.fluent.Expectations.value;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
Expand Down Expand Up @@ -79,7 +80,7 @@ public void beforeAll(ExtensionContext context) {
validateReferencesOfConstraints(errors, parameterSpaceSpec, knownNames);
require(value(errors).satisfies().empty());

List<TestData> testDataSet = Utils.generateTestDataSet(pipelineSpec, parameterSpaceSpec.toParameterSpace());
List<TestData> testDataSet = Utils.generateTestDataSet(pipelineSpec, parameterSpaceSpec.toParameterSpace(asList("param2", "param2")));
context.getStore(namespace).put("testDataSet", testDataSet);
context.getStore(namespace).put("parameterSpaceSpec", parameterSpaceSpec);
context.getStore(namespace).put("definedPredicates", definedPredicates);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void withdraw(int amount) {
throw new IllegalArgumentException("Invalid amount (non-positive): " + amount);
}
if (amount > balance)
throw new InsufficientBalance();
throw new InsufficientBalance("Tried to: " + balance + "-" + amount);
balance -= amount;
}

Expand All @@ -36,5 +36,8 @@ int getBalance() {
}

static class InsufficientBalance extends RuntimeException {
public InsufficientBalance(String message) {
super(message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static boolean startingWithSalute(@From("param1") String param1) {
}

@JCUnitTest
@Given("startingWithSalute")
//@Given("startingWithSalute")
public void testMethod(@From("param1") String param1, @From("param2") Map<String, List<String>> param2) {
System.out.println("param1:" + param1 + ", param2:" + param2);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.github.jcunit.annotations.*;
import com.github.jcunit.annotations.ConfigurePipelineWith.Entry;
import com.github.jcunit.model.ValueResolver;
import com.github.jcunit.pipeline.Pipeline;
import com.github.jcunit.runners.junit5.JCUnitTestEngine;
import org.junit.jupiter.api.extension.ExtendWith;

Expand Down
Loading