Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test specific hardware features with Robolectric on android?

I started writing a robolectric test around hardware specific features, like sensors and camera (front and back).

Imagine this class:

class CheckHardware {

    private bolean hasCamera(Context context) {
        PackageManager pm = context.callingActivityContext
            .getPackageManager();
        // camera support
        Boolean frontCam = pm.hasSystemFeature("android.hardware.camera.front");
        Boolean rearCam = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
        if (frontCam || rearCam) {
            return true;
        }
        return false;
    }
}

So i want to test different scenarios where there is a front camera and a rear camera, only a front camera or no camera at all. In my app its a bit more complicated but hope this makes it easier to understand what i mean.

For now i did it like this, which feels a bit odd.

RobolectricPackageManager pm = (RobolectricPackageManager) Robolectric.application.getPackageManager();
pm.setSystemFeature(PackageManager.FEATURE_CAMERA, true);

I thought about writing my own test runner, so for all expected hardware settings a specific runner like this

public class WithCameraTestRunner extends RobolectricTestRunner {   
  @Override
  public void setupApplicationstate(RobolectricConfig robolectricConfig) {
    super.setupApplicationState(robolectricConfig);
    ShadowApplication shadowApplication = shadowOf(Robolectric.application);
    shadowApplication.setPackageName(robolectricConfig.getPackageName());
    RobolectricPackageManager pm = new RobolectricPackageManager(Robolectric.application, robolectricConfig)
    pm.setSystemFeature(PackageManager.FEATURE_CAMERA, true);
    shadowApplication.setPackageManager(pm);
  }
}

Not quite happy with that either as i want to test different scenarios in the same test.

Any ideas? Whats the best approach for this?

like image 656
Tosa Avatar asked Feb 07 '13 00:02

Tosa


1 Answers

This can be done with JUnit rules. I wrote this example while using JUnit 4.8.2.

Here is the test class:

public class CameraTest {

    @Rule
    public CameraRule rule = new CameraRule();

    @Test
    @EnableCamera
    public void testWithCamera() {
        Assert.assertTrue(CameraHolder.CAMERA);
    }

    @Test
    public void testWithoutCamera() {
        Assert.assertFalse(CameraHolder.CAMERA);
    }

}

Here is the rule:

import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;

public class CameraRule implements MethodRule {

    @Override
    public Statement apply(Statement base, FrameworkMethod method, Object target) {
        boolean camera = false;
        if (method.getAnnotation(EnableCamera.class) != null) {
            camera = true;
        }

        return new CameraStatement(camera, base);
    }

}

I included the imports so you can see from where they originated. Statement is an interface with only a function evaluate().

Here is the statement class:

import org.junit.runners.model.Statement;

public class CameraStatement extends Statement {

    private boolean mEnabled;
    private Statement mStatement;

    public CameraStatement(boolean enabled, Statement statement) {
        mEnabled = enabled;
        mStatement = statement;
    }

    @Override
    public void evaluate() throws Throwable {
        CameraHolder.CAMERA = mEnabled;
        mStatement.evaluate();
    }

}

Instead of an enabled boolean you can easily pass in an enumeration or a Set of enumerations for which features you want enabled. Make sure to disable all features that are not explicitly enabled. In this example, if you don't explicitly set enabled to true it will be false.

Here is the code for the annotation itself:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableCamera {
}

This would allow you to use this to annotate the function with @EnableCamera in addition to @Test. If you want to have multiple features on for the same test you can put multiple annotations on the function.

For the sake of completeness here is the implementation of CameraHolder (but you won't need this):

public class CameraHolder {
    public static boolean CAMERA = false;
}

I tried with robolectric, but my version of robolectric didn't have the function you were using:

RobolectricPackageManager pm = (RobolectricPackageManager) Robolectric.application.getPackageManager();
// My version of robolectric didn't have this function.
pm.setSystemFeature(PackageManager.FEATURE_CAMERA, true);
like image 197
Joe Avatar answered Nov 14 '22 22:11

Joe