package cucumber.runtime.android;
import com.google.common.collect.Lists;
import cucumber.runtime.android.shadow.ShadowDexFile;
import cucumber.runtime.android.stub.unwanted.SomeUnwantedClass;
import cucumber.runtime.android.stub.wanted.Manifest;
import cucumber.runtime.android.stub.wanted.R;
import cucumber.runtime.android.stub.wanted.SomeClass;
import dalvik.system.DexFile;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import org.hamcrest.Matcher;
import org.hamcrest.collection.IsIterableContainingInOrder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertThat;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDexFile.class}, emulateSdk = 16, manifest = Config.NONE)
public class DexClassFinderTest {
private DexFile dexFile;
private DexClassFinder dexClassFinder;
@Before
public void beforeEachTest() throws IOException {
dexFile = new DexFile("notImportant");
dexClassFinder = new DexClassFinder(dexFile);
}
@Test
public void only_loads_classes_from_specified_package() throws Exception {
// given
setDexFileEntries(SomeClass.class, SomeUnwantedClass.class);
// when
final Collection<Class<?>> descendants = dexClassFinder.getDescendants(Object.class, SomeClass.class.getPackage().getName());
// then
assertThat(descendants, containsOnly(SomeClass.class));
}
@Test
public void does_not_load_manifest_class() throws Exception {
// given
setDexFileEntries(SomeClass.class, Manifest.class);
// when
final Collection<Class<?>> descendants = dexClassFinder.getDescendants(Object.class, SomeClass.class.getPackage().getName());
// then
assertThat(descendants, containsOnly(SomeClass.class));
}
@Test
public void does_not_load_R_class() throws Exception {
// given
setDexFileEntries(SomeClass.class, R.class);
// when
final Collection<Class<?>> descendants = dexClassFinder.getDescendants(Object.class, SomeClass.class.getPackage().getName());
// then
assertThat(descendants, containsOnly(SomeClass.class));
}
@Test
public void does_not_load_R_inner_class() throws Exception {
// given
setDexFileEntries(SomeClass.class, R.SomeInnerClass.class);
// when
final Collection<Class<?>> descendants = dexClassFinder.getDescendants(Object.class, SomeClass.class.getPackage().getName());
// then
assertThat(descendants, containsOnly(SomeClass.class));
}
@Test
public void only_loads_class_which_is_not_the_parent_type() throws Exception {
// given
setDexFileEntries(Integer.class, Number.class);
// when
final Class parentType = Number.class;
@SuppressWarnings("unchecked")
final Collection<Class<?>> descendants = dexClassFinder.getDescendants(parentType, Object.class.getPackage().getName());
// then
assertThat(descendants, containsOnly(Integer.class));
}
@Test
public void only_loads_class_which_is_assignable_to_parent_type() throws Exception {
// given
setDexFileEntries(Integer.class, String.class);
// when
final Class parentType = Number.class;
@SuppressWarnings("unchecked")
final Collection<Class<?>> descendants = dexClassFinder.getDescendants(parentType, Object.class.getPackage().getName());
// then
assertThat(descendants, containsOnly(Integer.class));
}
private Matcher<Iterable<? extends Class<?>>> containsOnly(final Class<?> type) {
return IsIterableContainingInOrder.<Class<?>>contains(type);
}
private void setDexFileEntries(final Class... entryClasses) throws NoSuchFieldException, IllegalAccessException {
final Field roboData = DexFile.class.getDeclaredField("__robo_data__");
final ShadowDexFile shadowDexFile = (ShadowDexFile) roboData.get(dexFile);
shadowDexFile.setEntries(classToName(entryClasses));
}
private Collection<String> classToName(final Class... entryClasses) {
final List<String> names = Lists.newArrayList();
for (final Class entryClass : entryClasses) {
names.add(entryClass.getName());
}
return names;
}
}