/*
* Copyright 2014 Stefan Mandel, Urs Metz
*
* 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 org.pitest.mutationtest.engine.gregor.mutators;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator.ARGUMENT_PROPAGATION_MUTATOR;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import org.junit.Before;
import org.junit.Test;
import org.pitest.mutationtest.engine.Mutant;
import org.pitest.mutationtest.engine.gregor.MutatorTestBase;
public class ArgumentPropagationMutatorTest extends MutatorTestBase {
@Before
public void setupEngineToUseReplaceMethodWithArgumentOfSameTypeAsReturnValueMutator() {
createTesteeWith(mutateOnlyCallMethod(), ARGUMENT_PROPAGATION_MUTATOR);
}
@Test
public void shouldReplaceMethodCallWithStringArgument() throws Exception {
final Mutant mutant = getFirstMutant(HasStringMethodCall.class);
assertMutantCallableReturns(new HasStringMethodCall("example"), mutant,
"example");
}
private static class HasStringMethodCall implements Callable<String> {
private final String arg;
public HasStringMethodCall(String arg) {
this.arg = arg;
}
public String delegate(final String aString) {
return "abc" + aString;
}
@Override
public String call() throws Exception {
return delegate(this.arg);
}
}
@Test
public void shouldReplaceMethodCallWithIntArgument() throws Exception {
final Mutant mutant = getFirstMutant(HasIntMethodCall.class);
assertMutantCallableReturns(new HasIntMethodCall(20), mutant, "20");
}
private static class HasIntMethodCall implements Callable<String> {
private final int arg;
public HasIntMethodCall(int arg) {
this.arg = arg;
}
public int delegate(int aInt) {
return 22 + aInt;
}
@Override
public String call() throws Exception {
return String.valueOf(delegate(this.arg));
}
}
@Test
public void shouldReplaceMethodCallWithLongArgument() throws Exception {
final Mutant mutant = getFirstMutant(HasLongMethodCall.class);
assertMutantCallableReturns(new HasLongMethodCall(20L), mutant, "20");
}
private static class HasLongMethodCall implements Callable<String> {
private final long arg;
public HasLongMethodCall(long arg) {
this.arg = arg;
}
public long delegate(long argument) {
return 22L + argument;
}
@Override
public String call() throws Exception {
return String.valueOf(delegate(this.arg));
}
}
@Test
public void shouldNotMutateMethodThatReturnsDifferentType() throws Exception {
assertNoMutants(ReturnsDifferentType.class);
}
class ReturnsDifferentType implements Callable<String> {
@Override
public String call() {
return addThreeAndConvertToString(3);
}
private String addThreeAndConvertToString(int argument) {
return String.valueOf(3 + argument);
}
}
@Test
public void continuesUntilMatchingArgumentTypeIsFound() throws Exception {
Mutant mutant = getFirstMutant(OnlyFirstArgumentHasMatchingType.class);
assertMutantCallableReturns(new OnlyFirstArgumentHasMatchingType("abc",
new Object(), 3), mutant, "abc");
}
private class OnlyFirstArgumentHasMatchingType implements Callable<String> {
private final String aString;
private final Object anObject;
private final long aLong;
public OnlyFirstArgumentHasMatchingType(String aString, Object anObject,
long aLong) {
this.aString = aString;
this.anObject = anObject;
this.aLong = aLong;
}
@Override
public String call() throws Exception {
return aMethod(this.aString, this.anObject, this.aLong);
}
private String aMethod(String aString, Object anObject, long aLong) {
return String.valueOf(anObject) + aString + String.valueOf(aLong);
}
}
@Test
public void usesLastArgumentOfMatchingTypeToReplaceMethod() throws Exception {
Mutant mutant = getFirstMutant(HasSeveralArgumentWithMatchingType.class);
assertMutantCallableReturns(new HasSeveralArgumentWithMatchingType(11, 22),
mutant, "22");
}
private class HasSeveralArgumentWithMatchingType implements Callable<String> {
private final int int1;
private final int int2;
public HasSeveralArgumentWithMatchingType(int i, int j) {
this.int1 = i;
this.int2 = j;
}
@Override
public String call() throws Exception {
String anInt = "3";
return String.valueOf(aMethod(this.int1, anInt, this.int2));
}
private int aMethod(int int1, String aString, int int2) {
return int1 + int2;
}
}
@Test
public void alsoReplaceCallToMethodWhenReturnValueIsNotUsed()
throws Exception {
Mutant mutant = getFirstMutant(ReturnValueNotUsed.class);
assertMutantCallableReturns(new ReturnValueNotUsed(), mutant, false);
}
private class ReturnValueNotUsed implements Callable<Boolean> {
private final List<String> aList = asList("xyz");
@Override
public Boolean call() throws Exception {
this.aList.set(0, "will not be present in list in mutated version");
return this.aList
.contains("will not be present in list in mutated version");
}
}
@Test
public void shouldReplaceMethodsReturningArraysMatchingArgumentType()
throws Exception {
final Mutant mutant = getFirstMutant(HasArrayMethod.class);
String[] expected = { "1", "2" };
String[] actual = mutateAndCall(new HasArrayMethod(), mutant);
assertThat(actual).containsExactly(expected);
}
private static class HasArrayMethod implements Callable<String[]> {
public String[] delegate(final String[] ss) {
return new String[] {};
}
@Override
public String[] call() throws Exception {
String[] s = { "1", "2" };
return delegate(s);
}
}
@Test
public void shouldNotReplaceMethodsReturningArraysOfUnmatchedType()
throws Exception {
assertNoMutants(HasArrayMethodOfDifferentType.class);
}
private static class HasArrayMethodOfDifferentType implements
Callable<String[]> {
public String[] delegate(final Integer[] ss) {
return new String[] {};
}
@Override
public String[] call() throws Exception {
Integer[] s = { 1, 2 };
return delegate(s);
}
}
@Test
public void willSubstituteCollectionsOfDifferentTypesDueToTypeErasure()
throws Exception {
final Mutant mutant = getFirstMutant(HasListMethod.class);
List<String> expected = Collections.emptyList();
List<String> actual = mutateAndCall(new HasListMethod(), mutant);
assertThat(actual).isEqualTo(expected);
}
private static class HasListMethod implements Callable<List<String>> {
public List<String> delegate(final List<Integer> is) {
return Arrays.asList(new String[] { "foo", "bar" });
}
@Override
public List<String> call() throws Exception {
List<Integer> s = Collections.emptyList();
return delegate(s);
}
}
@Test
public void shouldReplaceInstanceMethodCallThatIsUsedAsArgumentForCallToOtherObject()
throws Exception {
final Mutant mutant = getFirstMutant(CallsOtherObjectWithResultOfInstanceMethod.class);
MyListener listener = new MyListener();
assertMutantCallableReturns(new CallsOtherObjectWithResultOfInstanceMethod(
"lowercase", listener), mutant, "lowercase");
}
private class CallsOtherObjectWithResultOfInstanceMethod implements
Callable<String> {
private final String arg;
private final MyListener listener;
public CallsOtherObjectWithResultOfInstanceMethod(String arg,
MyListener listener) {
this.arg = arg;
this.listener = listener;
}
private String delegate(String aString) {
return aString.toUpperCase();
}
@Override
public String call() throws Exception {
this.listener.call(delegate(this.arg));
return this.listener.getCalledWith();
}
}
@Test
public void shouldReplaceStaticMethodCallThatIsUsedAsArgumentForCallToOtherObject()
throws Exception {
final Mutant mutant = getFirstMutant(CallsOtherObjectWithResultOfStaticMethod.class);
MyListener listener = new MyListener();
assertMutantCallableReturns(new CallsOtherObjectWithResultOfStaticMethod(
"lowercase", listener), mutant, "lowercase");
}
private static class CallsOtherObjectWithResultOfStaticMethod implements
Callable<String> {
private final String arg;
private final MyListener listener;
public CallsOtherObjectWithResultOfStaticMethod(String arg,
MyListener listener) {
this.arg = arg;
this.listener = listener;
}
private static String delegate(int i, String aString, long l) {
return aString.toUpperCase();
}
@Override
public String call() throws Exception {
this.listener.call(delegate(3, this.arg, 5L));
return this.listener.getCalledWith();
}
}
@Test
public void shouldReplaceInstanceMethodCallWithSeveralArgumentsThatIsUsedAsArgumentForCallToOtherObject()
throws Exception {
final Mutant mutant = getFirstMutant(CallsOtherObjectWithResultOfInstanceMethodHavingSeveralArguments.class);
MyListener listener = new MyListener();
assertMutantCallableReturns(
new CallsOtherObjectWithResultOfInstanceMethodHavingSeveralArguments(
"lowercase", listener), mutant, "lowercase");
}
private static class CallsOtherObjectWithResultOfInstanceMethodHavingSeveralArguments
implements Callable<String> {
private final String arg;
private final MyListener listener;
public CallsOtherObjectWithResultOfInstanceMethodHavingSeveralArguments(
String arg, MyListener listener) {
this.arg = arg;
this.listener = listener;
}
private String delegate(int i, double aDouble, Object object,
String aString, long l) {
return aString.toUpperCase();
}
@Override
public String call() throws Exception {
this.listener.call(delegate(3, 4.2D, new Object(), this.arg, 5L));
return this.listener.getCalledWith();
}
}
private static class MyListener {
private String calledWith = "not called";
public void call(String text) {
this.calledWith = text;
}
public String getCalledWith() {
return this.calledWith;
}
}
}