/*
* Copyright 2010 Henry Coles
*
* 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.logging.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.TraceClassVisitor;
import org.pitest.classinfo.ClassByteArraySource;
import org.pitest.classinfo.ClassName;
import org.pitest.classpath.ClassPathByteArraySource;
import org.pitest.functional.F;
import org.pitest.functional.FCollection;
import org.pitest.functional.FunctionalList;
import org.pitest.functional.predicate.Predicate;
import org.pitest.functional.predicate.True;
import org.pitest.mutationtest.engine.Mutant;
import org.pitest.mutationtest.engine.MutationDetails;
import org.pitest.mutationtest.engine.gregor.inlinedcode.InlinedCodeFilter;
import org.pitest.mutationtest.engine.gregor.inlinedcode.NoInlinedCodeDetection;
import org.pitest.simpletest.ExcludedPrefixIsolationStrategy;
import org.pitest.simpletest.Transformation;
import org.pitest.simpletest.TransformingClassLoader;
import org.pitest.util.IsolationUtils;
import org.pitest.util.Unchecked;
public abstract class MutatorTestBase {
protected GregorMutater engine;
protected FunctionalList<MutationDetails> findMutationsFor(
final Class<?> clazz) {
return this.engine.findMutations(new ClassName(clazz));
}
protected FunctionalList<MutationDetails> findMutationsFor(final String clazz) {
return this.engine.findMutations(new ClassName(clazz));
}
protected void createTesteeWith(final Predicate<MethodInfo> filter,
final MethodMutatorFactory... mutators) {
this.engine = new GregorMutater(new ClassPathByteArraySource(), filter,
Arrays.asList(mutators), filteredClasses(), inlinedCodeFilter());
}
private Collection<String> filteredClasses() {
return Arrays.asList(Logger.class.getName(), StringBuilder.class.getName());
}
protected void createTesteeWith(final ClassByteArraySource source,
final Predicate<MethodInfo> filter,
final Collection<MethodMutatorFactory> mutators) {
this.engine = new GregorMutater(source, filter, mutators,
filteredClasses(), inlinedCodeFilter());
}
protected void createTesteeWith(final Predicate<MethodInfo> filter,
final Collection<MethodMutatorFactory> mutators) {
createTesteeWith(new ClassPathByteArraySource(), filter, mutators);
}
protected void createTesteeWith(final Predicate<MethodInfo> filter,
final Collection<String> loggingClasses,
final Collection<MethodMutatorFactory> mutators) {
this.engine = new GregorMutater(new ClassPathByteArraySource(), filter,
mutators, loggingClasses, inlinedCodeFilter());
}
private InlinedCodeFilter inlinedCodeFilter() {
return new NoInlinedCodeDetection();
}
protected void createTesteeWith(
final Collection<MethodMutatorFactory> mutators) {
createTesteeWith(True.<MethodInfo> all(), mutators);
}
protected void createTesteeWith(final MethodMutatorFactory... mutators) {
createTesteeWith(True.<MethodInfo> all(), mutators);
}
protected <T> void assertMutantCallableReturns(final Callable<T> unmutated,
final Mutant mutant, final T expected) throws Exception {
assertEquals(expected, mutateAndCall(unmutated, mutant));
}
protected void assertNoMutants(final Class<?> mutee) {
final Collection<MutationDetails> actual = findMutationsFor(mutee);
assertTrue(actual.isEmpty());
}
protected <T> T mutateAndCall(final Callable<T> unmutated, final Mutant mutant) {
try {
final ClassLoader loader = createClassLoader(mutant);
return runInClassLoader(loader, unmutated);
} catch (final RuntimeException ex) {
throw ex;
} catch (final Exception ex) {
throw Unchecked.translateCheckedException(ex);
}
}
private ClassLoader createClassLoader(final Mutant mutant) throws Exception {
final TransformingClassLoader loader = new TransformingClassLoader(
createTransformation(mutant), new ExcludedPrefixIsolationStrategy());
return loader;
}
private Transformation createTransformation(final Mutant mutant) {
return new Transformation() {
@Override
public byte[] transform(final String name, final byte[] bytes) {
if (name.equals(mutant.getDetails().getClassName().asJavaName())) {
return mutant.getBytes();
} else {
return bytes;
}
}
};
}
@SuppressWarnings("unchecked")
private <T> T runInClassLoader(final ClassLoader loader,
final Callable<T> callable) throws Exception {
final Callable<T> c = (Callable<T>) IsolationUtils.cloneForLoader(callable,
loader);
return c.call();
}
protected List<Mutant> getMutants(
final FunctionalList<MutationDetails> details) {
return details.map(createMutant());
}
private F<MutationDetails, Mutant> createMutant() {
return new F<MutationDetails, Mutant>() {
@Override
public Mutant apply(final MutationDetails a) {
return MutatorTestBase.this.engine.getMutation(a.getId());
}
};
}
protected Mutant getFirstMutant(final Collection<MutationDetails> actual) {
assertFalse("No mutant found", actual.isEmpty());
final Mutant mutant = this.engine.getMutation(actual.iterator().next()
.getId());
verifyMutant(mutant);
return mutant;
}
protected Mutant getFirstMutant(final Class<?> mutee) {
final Collection<MutationDetails> actual = findMutationsFor(mutee);
return getFirstMutant(actual);
}
private void verifyMutant(final Mutant mutant) {
// printMutant(mutant);
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
CheckClassAdapter.verify(new ClassReader(mutant.getBytes()), false, pw);
assertTrue(sw.toString(), sw.toString().length() == 0);
}
protected void printMutant(final Mutant mutant) {
final ClassReader reader = new ClassReader(mutant.getBytes());
reader.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(
System.out)), ClassReader.EXPAND_FRAMES);
}
protected void assertMutantsReturn(final Callable<String> mutee,
final FunctionalList<MutationDetails> details,
final String... expectedResults) {
final List<Mutant> mutants = this.getMutants(details);
assertEquals("Should return one mutant for each request", details.size(),
mutants.size());
final FunctionalList<String> results = FCollection.map(mutants,
mutantToStringReults(mutee));
int i = 0;
for (final String actual : results) {
assertEquals(expectedResults[i], actual);
i++;
}
}
private F<Mutant, String> mutantToStringReults(final Callable<String> mutee) {
return new F<Mutant, String>() {
@Override
public String apply(final Mutant mutant) {
return mutateAndCall(mutee, mutant);
}
};
}
protected void assertMutantsAreFrom(
final FunctionalList<MutationDetails> actualDetails,
final Class<?>... mutators) {
assertEquals(mutators.length, actualDetails.size());
int i = 0;
for (final MutationDetails each : actualDetails) {
assertEquals(each.getId().getMutator(), mutators[i].getName());
i++;
}
}
protected Mutant createFirstMutant(
final Class<? extends Callable<String>> mutee) {
final Collection<MutationDetails> actual = findMutationsFor(mutee);
return getFirstMutant(actual);
}
protected Predicate<MethodInfo> mutateOnlyCallMethod() {
return new Predicate<MethodInfo>() {
@Override
public Boolean apply(final MethodInfo a) {
return a.getName().equals("call");
}
};
}
protected F<MutationDetails, Boolean> descriptionContaining(final String value) {
return new F<MutationDetails, Boolean>() {
@Override
public Boolean apply(final MutationDetails a) {
return a.getDescription().contains(value);
}
};
}
}