MethodYield { public Set<Flow> flow(List<Integer> parameterIndices, List<Class<? extends Constraint>> domains, int maxReturnedFlows) { Preconditions.checkArgument(!parameterIndices.isEmpty(), "computing flow on empty symbolic value list should never happen"); if(node == null || behavior == null) { return Collections.emptySet(); } String key = parameterIndices.stream().sorted().map(Object::toString).collect(Collectors.joining(",")); String domainKey = domains.stream().map(Class::getName).sorted().reduce("", String::concat); Map<String, Set<Flow>> flowByDomain = cachedFlows.computeIfAbsent(key, k -> new HashMap<>()); return flowByDomain.computeIfAbsent(domainKey, k -> { Set<SymbolicValue> symbolicValues = getSymbolicValues(parameterIndices); Set<Symbol> trackedSymbols = getReturnSymbolAsTrackedSymbols(parameterIndices); return FlowComputation.flow(node, symbolicValues, c -> true, c -> false, domains, trackedSymbols, maxReturnedFlows); }); } protected  MethodYield(MethodBehavior behavior); protected  MethodYield(ExplodedGraph.Node node, MethodBehavior behavior); abstract Stream<ProgramState> statesAfterInvocation(List<SymbolicValue> invocationArguments, List<Type> invocationTypes, ProgramState programState,
    Supplier<SymbolicValue> svSupplier); Stream<ProgramState> parametersAfterInvocation(List<SymbolicValue> invocationArguments, List<Type> invocationTypes, ProgramState programState); boolean generatedByCheck(SECheck check); MethodBehavior methodBehavior(); @Override abstract String toString(); @Override int hashCode(); @Override boolean equals(Object obj); Set<Flow> flow(List<Integer> parameterIndices, List<Class<? extends Constraint>> domains, int maxReturnedFlows);  }
@Test void test_creation_of_flows() throws Exception { SymbolicExecutionVisitor sev = createSymbolicExecutionVisitor("src/test/files/se/XProcYieldsFlows.java"); MethodBehavior mb = getMethodBehavior(sev, "foo"); MethodYield methodYield = mb.happyPathYields() .filter(y -> y.resultConstraint() != null && y.resultConstraint().get(ObjectConstraint.class) != ObjectConstraint.NULL).findFirst().get(); Set<Flow> flowReturnValue = methodYield.flow(Collections.singletonList(-1), Lists.newArrayList(ObjectConstraint.class), FlowComputation.MAX_REPORTED_FLOWS); assertThat(flowReturnValue.iterator().next().isEmpty()).isFalse(); Set<Flow> flowFirstParam = methodYield.flow(Collections.singletonList(0), Lists.newArrayList(ObjectConstraint.class, BooleanConstraint.class), FlowComputation.MAX_REPORTED_FLOWS); assertThat(flowFirstParam.iterator().next().isEmpty()).isFalse(); }
@Test void flow_is_empty_when_yield_has_no_node() { MethodYield methodYield = new HappyPathYield(null, mockMethodBehavior(1, false)); assertThat(methodYield.flow(Collections.singletonList(0), Lists.newArrayList(ObjectConstraint.class, BooleanConstraint.class), FlowComputation.MAX_REPORTED_FLOWS)).isEmpty(); }
@Test void flow_should_fail_if_no_parameters_are_passed() throws Exception { MethodYield methodYield = new HappyPathYield(null, mockMethodBehavior(1, false)); List<Integer> parameters = Collections.emptyList(); List<Class<? extends Constraint>> domains = Collections.singletonList(ObjectConstraint.class); try { methodYield.flow(parameters, domains, FlowComputation.MAX_REPORTED_FLOWS); fail("calling flow with empty list should have failed"); } catch (IllegalArgumentException iae) { assertThat(iae).hasMessage("computing flow on empty symbolic value list should never happen"); } }