Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ You can also use command line to provide these options to cucumber-android. Here
android.defaultConfig.testInstrumentationRunner "cucumber.cukeulator.test.CukeulatorAndroidJUnitRunner"
```

### File-based tests

If, for some reason, you need to load features from the filesystem, use an absolute path when specifying the features options to @CucumberOptions (e.g. `features = "/data/local/tmp"`).

It is up to you to ensure your instrumentation has permission to read from the filesystem. You may need to add something like `<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />` to your AndroidManifest.xml.

### Debugging
Please read [the Android documentation on debugging](https://developer.android.com/tools/debugging/index.html).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import cucumber.runtime.CucumberException;
import cucumber.runtime.io.Resource;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.io.MultiLoader;

import java.io.IOException;
import java.net.URI;
Expand All @@ -26,17 +27,37 @@ final class AndroidResourceLoader implements ResourceLoader {
*/
private final Context context;

/**
* The {@link MultiLoader} to get the resources from.
*/
private final MultiLoader multiLoader;

/**
* Creates a new instance for the given parameter.
*
* @param context the {@link Context} to get resources from
*/
AndroidResourceLoader(final Context context) {
this(context, new MultiLoader(context.getClassLoader()));
}

/**
* Creates a new instance for the given parameter.
*
* @param context the {@link Context} to get resources from
* @param multiLoader the {@link Context} to get resources from
*/
AndroidResourceLoader(final Context context, final MultiLoader multiLoader) {
this.context = context;
this.multiLoader = multiLoader;
}

@Override
public Iterable<Resource> resources(final URI path, final String suffix) {
if (path.getScheme().equals("file") && path.getRawSchemeSpecificPart().startsWith("/")) {
return multiLoader.resources(path, suffix);
}

try {
final List<Resource> resources = new ArrayList<>();
final AssetManager assetManager = context.getAssets();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.content.Context;
import android.content.res.AssetManager;
import com.google.common.collect.Lists;
import cucumber.runtime.io.MultiLoader;
import cucumber.runtime.io.Resource;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
Expand All @@ -15,6 +16,7 @@

import java.io.IOException;
import java.net.URI;
import java.util.Iterator;
import java.util.List;

import static org.hamcrest.CoreMatchers.hasItem;
Expand All @@ -30,7 +32,12 @@ public class AndroidResourceLoaderTest {

private final Context context = mock(Context.class);
private final AssetManager assetManager = mock(AssetManager.class, RETURNS_SMART_NULLS);
private final AndroidResourceLoader androidResourceLoader = new AndroidResourceLoader(context);
private final MultiLoader multiLoader = mock(MultiLoader.class, RETURNS_SMART_NULLS);
private final Resource fileResource1 = mock(Resource.class, RETURNS_SMART_NULLS);
private final Resource fileResource2 = mock(Resource.class, RETURNS_SMART_NULLS);
private Iterable<Resource> fileResources = mock(Iterable.class, RETURNS_SMART_NULLS);
private Iterator<Resource> fileResourcesIterator = mock(Iterator.class, RETURNS_SMART_NULLS);
private final AndroidResourceLoader androidResourceLoader = new AndroidResourceLoader(context, multiLoader);

@Before
public void beforeEachTest() {
Expand Down Expand Up @@ -94,6 +101,81 @@ public void only_retrieves_those_resources_which_end_the_specified_suffix() thro
assertThat(resources, hasItem(withPath(path + "/" + expected)));
}

@Test
public void retrieves_file_resources_with_an_absolute_path() throws IOException {
// given
final String path = "file:/dir";
final String dir = "dir";
final String dirFile = "dir.feature";
final String subDir = "subdir";
final String subDirFile = "subdir.feature";
final String suffix = "feature";

when(fileResource1.getPath()).thenReturn(URI.create(path + "/" + dirFile));
when(fileResource2.getPath()).thenReturn(URI.create(path + "/" + subDir + "/" + subDirFile));
when(fileResourcesIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.TRUE, Boolean.FALSE);
when(fileResourcesIterator.next()).thenReturn(fileResource1, fileResource2);
when(fileResources.iterator()).thenReturn(fileResourcesIterator);
when(multiLoader.resources(URI.create(path), suffix)).thenReturn(fileResources);

// when
final List<Resource> resources = Lists.newArrayList(androidResourceLoader.resources(URI.create(path), suffix));

// then
assertThat(resources.size(), is(2));
assertThat(resources, hasItem(withPath(path + "/" + dirFile)));
assertThat(resources, hasItem(withPath(path + "/" + subDir + "/" + subDirFile)));
}

@Test
public void only_retrieves_android_resources_with_a_relative_path() throws IOException {
// given
final String dir = "dir";
String absPath = "file:/" + dir;
String relPath = "file:" + dir;
final String expected = "expected.feature";
final String unexpected = "unexpected.feature";
final String suffix = "feature";

when(assetManager.list(dir)).thenReturn(new String[]{expected});
when(fileResource1.getPath()).thenReturn(URI.create(absPath + "/" + unexpected));
when(fileResourcesIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
when(fileResourcesIterator.next()).thenReturn(fileResource1);
when(fileResources.iterator()).thenReturn(fileResourcesIterator);
when(multiLoader.resources(URI.create(absPath), suffix)).thenReturn(fileResources);

// when
final List<Resource> resources = Lists.newArrayList(androidResourceLoader.resources(URI.create(relPath), suffix));

// then
assertThat(resources.size(), is(1));
assertThat(resources, hasItem(withPath(relPath + "/" + expected)));
}

@Test
public void only_retrieves_file_resources_with_an_absolute_path() throws IOException {
// given
final String dir = "dir";
String absPath = "file:/" + dir;
final String expected = "expected.feature";
final String unexpected = "unexpected.feature";
final String suffix = "feature";

when(fileResource1.getPath()).thenReturn(URI.create(absPath + "/" + expected));
when(fileResourcesIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
when(fileResourcesIterator.next()).thenReturn(fileResource1);
when(fileResources.iterator()).thenReturn(fileResourcesIterator);
when(multiLoader.resources(URI.create(absPath), suffix)).thenReturn(fileResources);
when(assetManager.list(dir)).thenReturn(new String[]{unexpected});

// when
final List<Resource> resources = Lists.newArrayList(androidResourceLoader.resources(URI.create(absPath), suffix));

// then
assertThat(resources.size(), is(1));
assertThat(resources, hasItem(withPath(absPath + "/" + expected)));
}

private static Matcher<? super Resource> withPath(final String path) {
return new TypeSafeMatcher<Resource>() {
@Override
Expand Down