/*
* 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.dependency;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.pitest.classpath.ClassPathByteArraySource;
import org.pitest.functional.predicate.Predicate;
import org.pitest.functional.predicate.True;
public class DependencyExtractorTest {
private DependencyExtractor testee;
public static class Foo {
public void one() {
new Bar();
}
public void two() {
new Car();
}
}
public static class Bar {
Far f = new Far();
}
public static class Car {
}
public static class Far {
VeryFar f = new VeryFar();
}
public static class VeryFar {
}
public static class CyclicFoo {
CyclicBar b = new CyclicBar();
}
public static class CyclicBar {
CyclicFoo f = new CyclicFoo();
}
@Test
public void shouldFindOnlyImmediateDependenciesWhenDepthIsOne()
throws Exception {
constructWithDepthOf(1);
final Collection<String> actual = this.testee
.extractCallDependenciesForPackages(Foo.class.getName(),
True.<String> all());
final Set<String> expected = asSet(classToJvmName(Bar.class),
classToJvmName(Car.class));
assertCollectionEquals(expected, actual);
}
@Test
public void shouldTraverseTwoLevelsOfDependenciesWhenDepthIsTwo()
throws Exception {
constructWithDepthOf(2);
final Collection<String> actual = this.testee
.extractCallDependenciesForPackages(Foo.class.getName(),
True.<String> all());
final Set<String> expected = asSet(classToJvmName(Bar.class),
classToJvmName(Car.class), classToJvmName(Far.class));
assertCollectionEquals(expected, actual);
}
@Test
public void shouldTraverseUnboundedWhenDepthIsZero() throws Exception {
constructWithDepthOf(0);
final Collection<String> actual = this.testee
.extractCallDependenciesForPackages(Foo.class.getName(),
True.<String> all());
final List<String> expected = Arrays.asList(classToJvmName(Bar.class),
classToJvmName(Car.class), classToJvmName(Far.class),
classToJvmName(VeryFar.class));
assertCollectionEquals(expected, actual);
}
@Test
public void shouldNotPickUpDependenciesFromFilteredMethods() throws Exception {
constructWithDepthOf(0);
final Collection<String> actual = this.testee.extractCallDependencies(
Foo.class.getName(), excludeMethodsCalledOne());
final Set<String> expected = asSet(classToJvmName(Car.class));
assertCollectionEquals(expected, actual);
}
@Test
public void shouldFindDependenciesReachedViaClassesNotMatchingFilter()
throws IOException {
constructWithDepthOf(5);
final Collection<String> actual = this.testee
.extractCallDependenciesForPackages(Foo.class.getName(),
includeOnlyThingsCalled("VeryFar"), ignoreCoreClasses());
final Set<String> expected = asSet(classToJvmName(VeryFar.class));
assertCollectionEquals(expected, actual);
}
@Test
public void shouldHandleCyclicDependencies() throws Exception {
constructWithDepthOf(0);
final Collection<String> actual = this.testee
.extractCallDependenciesForPackages(CyclicFoo.class.getName(),
True.<String> all());
final List<String> expected = Arrays
.asList(classToJvmName(CyclicBar.class));
assertCollectionEquals(expected, actual);
}
private Predicate<DependencyAccess> ignoreCoreClasses() {
return new Predicate<DependencyAccess>() {
@Override
public Boolean apply(final DependencyAccess a) {
return !a.getDest().getOwner().startsWith("java");
}
};
}
private Predicate<String> includeOnlyThingsCalled(final String subString) {
return new Predicate<String>() {
@Override
public Boolean apply(final String a) {
return a.contains(subString);
}
};
}
private void constructWithDepthOf(final int depth) {
this.testee = new DependencyExtractor(new ClassPathByteArraySource(), depth);
}
private Predicate<DependencyAccess> excludeMethodsCalledOne() {
return new Predicate<DependencyAccess>() {
@Override
public Boolean apply(final DependencyAccess a) {
return !a.getSource().getName().equals("one");
}
};
}
private void assertCollectionEquals(final Collection<String> expected,
final Collection<String> actual) {
final Set<String> expectedSet = new HashSet<String>();
expectedSet.addAll(expected);
final Set<String> actualSet = new HashSet<String>();
actualSet.addAll(actual);
assertEquals(expectedSet, actualSet);
}
private Set<String> asSet(final String... values) {
final Set<String> set = new HashSet<String>();
set.addAll(Arrays.asList(values));
return set;
}
private String classToJvmName(final Class<?> clazz) {
return clazz.getName().replace(".", "/");
}
}