Improve coverage (#22)
* New tests * Try adjust codecov * CropHelper and CameraUtils tests * Set target coverage to 40% * OrientationHelperTest * Preview tests * Fix SurfaceView * Introduce Task for async tests * Gesture tests * Improve Gesture Tests * Add wake lock * Fix view size * Add build info logs * Revert info * Try fix bug * Test timeout * Try fix tests * Fix TextureView tests if not hardware accelerated * Fix bugpull/36/head
parent
982ed3d094
commit
66077c1eaa
@ -0,0 +1,13 @@ |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
package="com.otaliastudios.cameraview"> |
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" /> |
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" /> |
||||
|
||||
<application> |
||||
<activity |
||||
android:hardwareAccelerated="true" |
||||
android:name=".TestActivity"/> |
||||
</application> |
||||
|
||||
</manifest> |
@ -0,0 +1,41 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.content.Context; |
||||
import android.os.Handler; |
||||
import android.os.Looper; |
||||
import android.support.test.InstrumentationRegistry; |
||||
import android.support.test.annotation.UiThreadTest; |
||||
import android.support.test.rule.ActivityTestRule; |
||||
import android.view.View; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Rule; |
||||
|
||||
public class BaseTest { |
||||
|
||||
public static void ui(Runnable runnable) { |
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable); |
||||
} |
||||
|
||||
public static void uiAsync(Runnable runnable) { |
||||
new Handler(Looper.getMainLooper()).post(runnable); |
||||
} |
||||
|
||||
public static Context context() { |
||||
return InstrumentationRegistry.getInstrumentation().getContext(); |
||||
} |
||||
|
||||
public static void uiRequestLayout(final View view) { |
||||
ui(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
view.requestLayout(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public static void waitUi() { |
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync(); |
||||
} |
||||
} |
@ -0,0 +1,60 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.annotation.TargetApi; |
||||
import android.app.Instrumentation; |
||||
import android.content.Context; |
||||
import android.content.pm.PackageManager; |
||||
import android.graphics.Bitmap; |
||||
import android.graphics.Color; |
||||
import android.support.test.InstrumentationRegistry; |
||||
import android.support.test.filters.SmallTest; |
||||
import android.support.test.internal.runner.InstrumentationConnection; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
|
||||
import static org.mockito.Mockito.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@SmallTest |
||||
public class CameraUtilsTest { |
||||
|
||||
@Test |
||||
public void testHasCameras() { |
||||
Context context = mock(Context.class); |
||||
PackageManager pm = mock(PackageManager.class); |
||||
when(context.getPackageManager()).thenReturn(pm); |
||||
when(pm.hasSystemFeature(anyString())).thenReturn(true); |
||||
assertTrue(CameraUtils.hasCameras(context)); |
||||
|
||||
when(pm.hasSystemFeature(anyString())).thenReturn(false); |
||||
assertFalse(CameraUtils.hasCameras(context)); |
||||
} |
||||
|
||||
@Test |
||||
public void testDecodeBitmap() { |
||||
int w = 100, h = 200, color = Color.WHITE; |
||||
Bitmap source = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); |
||||
source.setPixel(0, 0, color); |
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(); |
||||
|
||||
// Using lossy JPG we can't have strict comparison of values after compression.
|
||||
source.compress(Bitmap.CompressFormat.PNG, 100, os); |
||||
|
||||
// No orientation.
|
||||
Bitmap other = CameraUtils.decodeBitmap(os.toByteArray()); |
||||
assertEquals(100, w); |
||||
assertEquals(200, h); |
||||
assertEquals(color, other.getPixel(0, 0)); |
||||
assertEquals(0, other.getPixel(0, h-1)); |
||||
assertEquals(0, other.getPixel(w-1, 0)); |
||||
assertEquals(0, other.getPixel(w-1, h-1)); |
||||
|
||||
// TODO: improve when we add EXIF writing to byte arrays
|
||||
} |
||||
} |
@ -0,0 +1,92 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.content.Context; |
||||
import android.content.pm.PackageManager; |
||||
import android.graphics.Bitmap; |
||||
import android.graphics.BitmapFactory; |
||||
import android.graphics.Color; |
||||
import android.graphics.Rect; |
||||
import android.graphics.YuvImage; |
||||
import android.support.test.filters.SmallTest; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.invocation.InvocationOnMock; |
||||
import org.mockito.stubbing.Answer; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.OutputStream; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertFalse; |
||||
import static org.junit.Assert.assertTrue; |
||||
import static org.mockito.Matchers.any; |
||||
import static org.mockito.Matchers.anyInt; |
||||
import static org.mockito.Mockito.anyString; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@SmallTest |
||||
public class CropHelperTest { |
||||
|
||||
@Test |
||||
public void testCropFromYuv() { |
||||
testCropFromYuv(1600, 1600, AspectRatio.of(16, 9)); |
||||
testCropFromYuv(1600, 1600, AspectRatio.of(9, 16)); |
||||
} |
||||
|
||||
private void testCropFromYuv(final int w, final int h, final AspectRatio target) { |
||||
final boolean wider = target.toFloat() > ((float) w / (float) h); |
||||
|
||||
// Not sure how to test YuvImages...
|
||||
YuvImage i = mock(YuvImage.class); |
||||
when(i.getWidth()).thenReturn(w); |
||||
when(i.getHeight()).thenReturn(h); |
||||
when(i.compressToJpeg(any(Rect.class), anyInt(), any(OutputStream.class))).thenAnswer(new Answer<Boolean>() { |
||||
@Override |
||||
public Boolean answer(InvocationOnMock iom) throws Throwable { |
||||
Object[] args = iom.getArguments(); |
||||
Rect rect = (Rect) args[0]; |
||||
|
||||
// Assert.
|
||||
AspectRatio ratio = AspectRatio.of(rect.width(), rect.height()); |
||||
assertEquals(target, ratio); |
||||
if (wider) { // width must match.
|
||||
assertEquals(rect.width(), w); |
||||
} else { |
||||
assertEquals(rect.height(), h); |
||||
} |
||||
return true; |
||||
} |
||||
}); |
||||
CropHelper.cropToJpeg(i, target, 100); |
||||
} |
||||
|
||||
@Test |
||||
public void testCropFromJpeg() { |
||||
testCropFromJpeg(1600, 1600, AspectRatio.of(16, 9)); |
||||
testCropFromJpeg(1600, 1600, AspectRatio.of(9, 16)); |
||||
} |
||||
|
||||
private void testCropFromJpeg(int w, int h, AspectRatio target) { |
||||
final boolean wider = target.toFloat() > ((float) w / (float) h); |
||||
|
||||
Bitmap source = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); |
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(); |
||||
source.compress(Bitmap.CompressFormat.JPEG, 100, os); |
||||
byte[] b = CropHelper.cropToJpeg(os.toByteArray(), target, 100); |
||||
Bitmap result = BitmapFactory.decodeByteArray(b, 0, b.length); |
||||
|
||||
// Assert.
|
||||
AspectRatio ratio = AspectRatio.of(result.getWidth(), result.getHeight()); |
||||
assertEquals(target, ratio); |
||||
if (wider) { // width must match.
|
||||
assertEquals(result.getWidth(), w); |
||||
} else { |
||||
assertEquals(result.getHeight(), h); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,62 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.annotation.TargetApi; |
||||
import android.content.Context; |
||||
import android.support.test.espresso.Espresso; |
||||
import android.support.test.espresso.Root; |
||||
import android.support.test.espresso.ViewAssertion; |
||||
import android.support.test.espresso.ViewInteraction; |
||||
import android.support.test.espresso.assertion.ViewAssertions; |
||||
import android.support.test.espresso.matcher.ViewMatchers; |
||||
import android.support.test.rule.ActivityTestRule; |
||||
import android.view.MotionEvent; |
||||
import android.view.View; |
||||
|
||||
import org.hamcrest.BaseMatcher; |
||||
import org.hamcrest.Description; |
||||
import org.hamcrest.Matchers; |
||||
import org.junit.Before; |
||||
import org.junit.Rule; |
||||
|
||||
import static android.support.test.espresso.Espresso.onView; |
||||
import static org.hamcrest.Matchers.any; |
||||
|
||||
@TargetApi(17) |
||||
public abstract class GestureLayoutTest<T extends GestureLayout> extends BaseTest { |
||||
|
||||
protected abstract T create(Context context); |
||||
|
||||
@Rule |
||||
public ActivityTestRule<TestActivity> rule = new ActivityTestRule<>(TestActivity.class); |
||||
|
||||
protected T layout; |
||||
protected Task<Gesture> touch; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
ui(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
TestActivity a = rule.getActivity(); |
||||
layout = create(a); |
||||
layout.enable(true); |
||||
a.inflate(layout); |
||||
|
||||
touch = new Task<>(); |
||||
layout.setOnTouchListener(new View.OnTouchListener() { |
||||
@Override |
||||
public boolean onTouch(View view, MotionEvent motionEvent) { |
||||
boolean found = layout.onTouchEvent(motionEvent); |
||||
if (found) touch.end(layout.getGestureType()); |
||||
return true; |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
protected final ViewInteraction onLayout() { |
||||
return onView(Matchers.<View>is(layout)).inRoot(any(Root.class)); |
||||
} |
||||
} |
@ -0,0 +1,78 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.graphics.Canvas; |
||||
import android.graphics.drawable.ColorDrawable; |
||||
import android.support.test.InstrumentationRegistry; |
||||
import android.support.test.filters.MediumTest; |
||||
import android.support.test.rule.ActivityTestRule; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import static org.mockito.Mockito.*; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@MediumTest |
||||
public class GridLinesLayoutTest extends BaseTest { |
||||
|
||||
@Rule |
||||
public ActivityTestRule<TestActivity> rule = new ActivityTestRule<>(TestActivity.class); |
||||
|
||||
private GridLinesLayout layout; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
ui(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
TestActivity a = rule.getActivity(); |
||||
layout = new GridLinesLayout(a); |
||||
layout.setGridMode(Grid.OFF); |
||||
a.getContentView().addView(layout); |
||||
layout.vert = mock(ColorDrawable.class); |
||||
layout.horiz = mock(ColorDrawable.class); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void testOff() { |
||||
layout.drawTask.listen(); |
||||
layout.setGridMode(Grid.OFF); |
||||
layout.drawTask.await(); |
||||
verify(layout.vert, never()).draw(any(Canvas.class)); |
||||
verify(layout.horiz, never()).draw(any(Canvas.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void test3x3() { |
||||
layout.drawTask.listen(); |
||||
layout.setGridMode(Grid.DRAW_3X3); |
||||
layout.drawTask.await(); |
||||
verify(layout.vert, times(2)).draw(any(Canvas.class)); |
||||
verify(layout.horiz, times(2)).draw(any(Canvas.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void testPhi() { |
||||
layout.drawTask.listen(); |
||||
layout.setGridMode(Grid.DRAW_PHI); |
||||
layout.drawTask.await(); |
||||
verify(layout.vert, times(2)).draw(any(Canvas.class)); |
||||
verify(layout.horiz, times(2)).draw(any(Canvas.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void test4x4() { |
||||
layout.drawTask.listen(); |
||||
layout.setGridMode(Grid.DRAW_4X4); |
||||
layout.drawTask.await(); |
||||
verify(layout.vert, times(3)).draw(any(Canvas.class)); |
||||
verify(layout.horiz, times(3)).draw(any(Canvas.class)); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,97 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.annotation.TargetApi; |
||||
import android.app.Instrumentation; |
||||
import android.app.UiAutomation; |
||||
import android.support.test.InstrumentationRegistry; |
||||
import android.support.test.filters.SmallTest; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
import android.view.OrientationEventListener; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@SmallTest |
||||
public class OrientationHelperTest extends BaseTest { |
||||
|
||||
private OrientationHelper helper; |
||||
private OrientationHelper.Callbacks callbacks; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
ui(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
callbacks = mock(OrientationHelper.Callbacks.class); |
||||
helper = new OrientationHelper(context(), callbacks); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@After |
||||
public void tearDown() { |
||||
callbacks = null; |
||||
helper = null; |
||||
} |
||||
|
||||
@Test |
||||
public void testEnable() { |
||||
assertNotNull(helper.mListener); |
||||
assertNull(helper.mDisplay); |
||||
|
||||
helper.enable(context()); |
||||
assertNotNull(helper.mListener); |
||||
assertNotNull(helper.mDisplay); |
||||
|
||||
// Ensure nothing bad if called twice.
|
||||
helper.enable(context()); |
||||
assertNotNull(helper.mListener); |
||||
assertNotNull(helper.mDisplay); |
||||
|
||||
helper.disable(); |
||||
assertNotNull(helper.mListener); |
||||
assertNull(helper.mDisplay); |
||||
|
||||
verify(callbacks, atLeastOnce()).onDisplayOffsetChanged(anyInt()); |
||||
} |
||||
|
||||
@Test |
||||
public void testRotation() { |
||||
helper.enable(context()); |
||||
|
||||
reset(callbacks); // Reset counts.
|
||||
helper.mListener.onOrientationChanged(OrientationEventListener.ORIENTATION_UNKNOWN); |
||||
assertEquals(helper.mLastOrientation, 0); |
||||
helper.mListener.onOrientationChanged(10); |
||||
assertEquals(helper.mLastOrientation, 0); |
||||
helper.mListener.onOrientationChanged(-10); |
||||
assertEquals(helper.mLastOrientation, 0); |
||||
helper.mListener.onOrientationChanged(44); |
||||
assertEquals(helper.mLastOrientation, 0); |
||||
helper.mListener.onOrientationChanged(360); |
||||
assertEquals(helper.mLastOrientation, 0); |
||||
|
||||
// Callback called just once.
|
||||
verify(callbacks, times(1)).onDeviceOrientationChanged(0); |
||||
|
||||
helper.mListener.onOrientationChanged(90); |
||||
helper.mListener.onOrientationChanged(91); |
||||
assertEquals(helper.mLastOrientation, 90); |
||||
verify(callbacks, times(1)).onDeviceOrientationChanged(90); |
||||
|
||||
helper.mListener.onOrientationChanged(180); |
||||
assertEquals(helper.mLastOrientation, 180); |
||||
verify(callbacks, times(1)).onDeviceOrientationChanged(180); |
||||
|
||||
helper.mListener.onOrientationChanged(270); |
||||
assertEquals(helper.mLastOrientation, 270); |
||||
verify(callbacks, times(1)).onDeviceOrientationChanged(270); |
||||
} |
||||
} |
@ -0,0 +1,86 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.content.Context; |
||||
import android.support.test.espresso.Espresso; |
||||
import android.support.test.espresso.UiController; |
||||
import android.support.test.espresso.ViewAction; |
||||
import android.support.test.espresso.action.CoordinatesProvider; |
||||
import android.support.test.espresso.action.GeneralLocation; |
||||
import android.support.test.espresso.action.GeneralSwipeAction; |
||||
import android.support.test.espresso.action.MotionEvents; |
||||
import android.support.test.espresso.action.PrecisionDescriber; |
||||
import android.support.test.espresso.action.Press; |
||||
import android.support.test.espresso.action.Swipe; |
||||
import android.support.test.espresso.action.ViewActions; |
||||
import android.support.test.espresso.assertion.ViewAssertions; |
||||
import android.support.test.espresso.matcher.ViewMatchers; |
||||
import android.support.test.filters.SmallTest; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
import android.view.MotionEvent; |
||||
import android.view.ScaleGestureDetector; |
||||
import android.view.View; |
||||
|
||||
import org.hamcrest.Matcher; |
||||
import org.hamcrest.Matchers; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import static android.support.test.espresso.Espresso.onView; |
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId; |
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertNotNull; |
||||
import static org.junit.Assert.assertNull; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@SmallTest |
||||
public class PinchGestureLayoutTest extends GestureLayoutTest<PinchGestureLayout> { |
||||
|
||||
@Override |
||||
protected PinchGestureLayout create(Context context) { |
||||
return new PinchGestureLayout(context); |
||||
} |
||||
|
||||
@Test |
||||
public void testDefaults() { |
||||
assertEquals(layout.getGestureType(), Gesture.PINCH); |
||||
assertEquals(layout.getPoints().length, 2); |
||||
assertEquals(layout.getPoints()[0].x, 0, 0); |
||||
assertEquals(layout.getPoints()[0].y, 0, 0); |
||||
assertEquals(layout.getPoints()[1].x, 0, 0); |
||||
assertEquals(layout.getPoints()[1].y, 0, 0); |
||||
} |
||||
|
||||
// TODO: test pinch open
|
||||
// TODO: test pinch close
|
||||
// TODO: test pinch disabled
|
||||
|
||||
// Possible approach: mimic pinch gesture and let espresso test.
|
||||
// Too lazy to do this now, but it's possible.
|
||||
// https://stackoverflow.com/questions/11523423/how-to-generate-zoom-pinch-gesture-for-testing-for-android
|
||||
|
||||
public abstract class PinchViewAction implements ViewAction { |
||||
} |
||||
|
||||
private void testPinch(ViewAction action, boolean increasing) { |
||||
touch.listen(); |
||||
touch.start(); |
||||
onLayout().perform(action); |
||||
Gesture found = touch.await(10000, TimeUnit.MILLISECONDS); |
||||
assertNotNull(found); |
||||
|
||||
// How will this move our parameter?
|
||||
float curr = 0.5f, min = 0f, max = 1f; |
||||
float newValue = layout.scaleValue(curr, min, max); |
||||
if (increasing) { |
||||
assertTrue(newValue > curr); |
||||
assertTrue(newValue <= max); |
||||
} else { |
||||
assertTrue(newValue < curr); |
||||
assertTrue(newValue >= min); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,157 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.content.Context; |
||||
import android.support.test.rule.ActivityTestRule; |
||||
import android.view.ViewGroup; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.mockito.invocation.InvocationOnMock; |
||||
import org.mockito.stubbing.Answer; |
||||
|
||||
|
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
import static org.mockito.Mockito.*; |
||||
|
||||
public abstract class PreviewTest extends BaseTest { |
||||
|
||||
protected abstract Preview createPreview(Context context, ViewGroup parent, Preview.SurfaceCallback callback); |
||||
|
||||
@Rule |
||||
public ActivityTestRule<TestActivity> rule = new ActivityTestRule<>(TestActivity.class); |
||||
|
||||
protected Preview preview; |
||||
protected Size surfaceSize; |
||||
private Preview.SurfaceCallback callback; |
||||
private Task<Boolean> availability; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
availability = new Task<>(); |
||||
availability.listen(); |
||||
|
||||
ui(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
TestActivity a = rule.getActivity(); |
||||
surfaceSize = a.getContentSize(); |
||||
|
||||
callback = mock(Preview.SurfaceCallback.class); |
||||
doAnswer(new Answer() { |
||||
@Override |
||||
public Object answer(InvocationOnMock invocation) throws Throwable { |
||||
if (availability != null) availability.end(true); |
||||
return null; |
||||
} |
||||
}).when(callback).onSurfaceAvailable(); |
||||
preview = createPreview(a, a.getContentView(), callback); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
// Wait for surface to be available.
|
||||
protected void ensureAvailable() { |
||||
assertNotNull(availability.await(2, TimeUnit.SECONDS)); |
||||
} |
||||
|
||||
// Trigger a destroy.
|
||||
protected void ensureDestroyed() { |
||||
ui(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
rule.getActivity().getContentView().removeView(preview.getView()); |
||||
} |
||||
}); |
||||
waitUi(); |
||||
} |
||||
|
||||
@After |
||||
public void tearDown() { |
||||
preview = null; |
||||
callback = null; |
||||
surfaceSize = null; |
||||
availability = null; |
||||
} |
||||
|
||||
@Test |
||||
public void testDefaults() { |
||||
ensureAvailable(); |
||||
assertTrue(preview.isReady()); |
||||
assertNotNull(preview.getView()); |
||||
assertNotNull(preview.getOutputClass()); |
||||
} |
||||
|
||||
@Test |
||||
public void testDesiredSize() { |
||||
preview.setDesiredSize(160, 90); |
||||
assertEquals(160, preview.getDesiredSize().getWidth()); |
||||
assertEquals(90, preview.getDesiredSize().getHeight()); |
||||
} |
||||
|
||||
@Test |
||||
public void testSurfaceAvailable() { |
||||
ensureAvailable(); |
||||
verify(callback, times(1)).onSurfaceAvailable(); |
||||
assertEquals(surfaceSize.getWidth(), preview.getSurfaceSize().getWidth()); |
||||
assertEquals(surfaceSize.getHeight(), preview.getSurfaceSize().getHeight()); |
||||
} |
||||
|
||||
@Test |
||||
public void testSurfaceDestroyed() { |
||||
ensureAvailable(); |
||||
ensureDestroyed(); |
||||
assertEquals(0, preview.getSurfaceSize().getWidth()); |
||||
assertEquals(0, preview.getSurfaceSize().getHeight()); |
||||
} |
||||
|
||||
@Test |
||||
public void testCropCenter() throws Exception { |
||||
ensureAvailable(); |
||||
|
||||
// This is given by the activity, it's the fixed size.
|
||||
float view = getViewAspectRatio(); |
||||
|
||||
// If we apply a desired size with same aspect ratio, there should be no crop.
|
||||
setDesiredAspectRatio(view); |
||||
assertFalse(preview.isCropping()); |
||||
|
||||
// If we apply a different aspect ratio, there should be cropping.
|
||||
float desired = view * 1.2f; |
||||
setDesiredAspectRatio(desired); |
||||
assertTrue(preview.isCropping()); |
||||
|
||||
// Since desired is 'desired', let's fake a new view size that is consistent with it.
|
||||
// Ensure crop is not happening anymore.
|
||||
preview.mCropTask.listen(); |
||||
preview.onSurfaceSizeChanged((int) (50f * desired), 50); // Wait...
|
||||
preview.mCropTask.await(); |
||||
assertEquals(desired, getViewAspectRatioWithScale(), 0.01f); |
||||
assertFalse(preview.isCropping()); |
||||
} |
||||
|
||||
private void setDesiredAspectRatio(float desiredAspectRatio) { |
||||
preview.mCropTask.listen(); |
||||
preview.setDesiredSize((int) (10f * desiredAspectRatio), 10); // Wait...
|
||||
preview.mCropTask.await(); |
||||
assertEquals(desiredAspectRatio, getViewAspectRatioWithScale(), 0.01f); |
||||
|
||||
} |
||||
|
||||
private float getViewAspectRatio() { |
||||
Size size = preview.getSurfaceSize(); |
||||
return AspectRatio.of(size.getWidth(), size.getHeight()).toFloat(); |
||||
} |
||||
|
||||
private float getViewAspectRatioWithScale() { |
||||
Size size = preview.getSurfaceSize(); |
||||
int newWidth = (int) (((float) size.getWidth()) * preview.getView().getScaleX()); |
||||
int newHeight = (int) (((float) size.getHeight()) * preview.getView().getScaleY()); |
||||
return AspectRatio.of(newWidth, newHeight).toFloat(); |
||||
} |
||||
} |
@ -0,0 +1,97 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.content.Context; |
||||
import android.support.test.espresso.ViewAction; |
||||
import android.support.test.espresso.assertion.ViewAssertions; |
||||
import android.support.test.filters.SmallTest; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
|
||||
import org.hamcrest.Matchers; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import static android.support.test.espresso.Espresso.onView; |
||||
import static android.support.test.espresso.action.ViewActions.click; |
||||
import static android.support.test.espresso.action.ViewActions.swipeDown; |
||||
import static android.support.test.espresso.action.ViewActions.swipeLeft; |
||||
import static android.support.test.espresso.action.ViewActions.swipeRight; |
||||
import static android.support.test.espresso.action.ViewActions.swipeUp; |
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId; |
||||
import static junit.framework.Assert.assertNotNull; |
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertNull; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@SmallTest |
||||
public class ScrollGestureLayoutTest extends GestureLayoutTest<ScrollGestureLayout> { |
||||
|
||||
@Override |
||||
protected ScrollGestureLayout create(Context context) { |
||||
return new ScrollGestureLayout(context); |
||||
} |
||||
|
||||
@Test |
||||
public void testDefaults() { |
||||
assertNull(layout.getGestureType()); |
||||
assertEquals(layout.getPoints().length, 2); |
||||
assertEquals(layout.getPoints()[0].x, 0, 0); |
||||
assertEquals(layout.getPoints()[0].y, 0, 0); |
||||
assertEquals(layout.getPoints()[1].x, 0, 0); |
||||
assertEquals(layout.getPoints()[1].y, 0, 0); |
||||
} |
||||
|
||||
@Test |
||||
public void testScrollDisabled() { |
||||
layout.enable(false); |
||||
touch.listen(); |
||||
touch.start(); |
||||
onLayout().perform(swipeUp()); |
||||
Gesture found = touch.await(500, TimeUnit.MILLISECONDS); |
||||
assertNull(found); |
||||
} |
||||
|
||||
private void testScroll(ViewAction scroll, Gesture expected, boolean increasing) { |
||||
touch.listen(); |
||||
touch.start(); |
||||
onLayout().perform(scroll); |
||||
Gesture found = touch.await(500, TimeUnit.MILLISECONDS); |
||||
assertEquals(found, expected); |
||||
|
||||
// How will this move our parameter?
|
||||
float curr = 0.5f, min = 0f, max = 1f; |
||||
float newValue = layout.scaleValue(curr, min, max); |
||||
if (increasing) { |
||||
assertTrue(newValue > curr); |
||||
assertTrue(newValue <= max); |
||||
} else { |
||||
assertTrue(newValue < curr); |
||||
assertTrue(newValue >= min); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testScrollLeft() { |
||||
testScroll(swipeLeft(), Gesture.SCROLL_HORIZONTAL, false); |
||||
} |
||||
|
||||
@Test |
||||
public void testScrollRight() { |
||||
testScroll(swipeRight(), Gesture.SCROLL_HORIZONTAL, true); |
||||
} |
||||
|
||||
@Test |
||||
public void testScrollUp() { |
||||
testScroll(swipeUp(), Gesture.SCROLL_VERTICAL, true); |
||||
} |
||||
|
||||
@Test |
||||
public void testScrollDown() { |
||||
testScroll(swipeDown(), Gesture.SCROLL_VERTICAL, false); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,19 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.content.Context; |
||||
import android.support.test.filters.SmallTest; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
import android.view.ViewGroup; |
||||
|
||||
import org.junit.runner.RunWith; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@SmallTest |
||||
public class SurfaceViewPreviewTest extends PreviewTest { |
||||
|
||||
@Override |
||||
protected Preview createPreview(Context context, ViewGroup parent, Preview.SurfaceCallback callback) { |
||||
return new SurfaceViewPreview(context, parent, callback); |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.content.Context; |
||||
import android.support.test.espresso.action.GeneralClickAction; |
||||
import android.support.test.espresso.action.GeneralLocation; |
||||
import android.support.test.espresso.action.Press; |
||||
import android.support.test.espresso.action.Tap; |
||||
import android.support.test.filters.SmallTest; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
import android.view.InputDevice; |
||||
import android.view.MotionEvent; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
|
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import static android.support.test.espresso.Espresso.*; |
||||
import static android.support.test.espresso.matcher.ViewMatchers.*; |
||||
import static android.support.test.espresso.action.ViewActions.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@SmallTest |
||||
public class TapGestureLayoutTest extends GestureLayoutTest<TapGestureLayout> { |
||||
|
||||
@Override |
||||
protected TapGestureLayout create(Context context) { |
||||
return new TapGestureLayout(context); |
||||
} |
||||
|
||||
@Test |
||||
public void testDefaults() { |
||||
assertNull(layout.getGestureType()); |
||||
assertEquals(layout.getPoints().length, 1); |
||||
assertEquals(layout.getPoints()[0].x, 0, 0); |
||||
assertEquals(layout.getPoints()[0].y, 0, 0); |
||||
} |
||||
|
||||
@Test |
||||
public void testTap() { |
||||
touch.listen(); |
||||
touch.start(); |
||||
GeneralClickAction a = new GeneralClickAction( |
||||
Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER, |
||||
InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY); |
||||
onLayout().perform(a); |
||||
Gesture found = touch.await(500, TimeUnit.MILLISECONDS); |
||||
|
||||
assertEquals(found, Gesture.TAP); |
||||
Size size = rule.getActivity().getContentSize(); |
||||
assertEquals(layout.getPoints()[0].x, (size.getWidth() / 2f), 1f); |
||||
assertEquals(layout.getPoints()[0].y, (size.getHeight() / 2f), 1f); |
||||
} |
||||
|
||||
@Test |
||||
public void testTapWhileDisabled() { |
||||
layout.enable(false); |
||||
touch.listen(); |
||||
touch.start(); |
||||
onLayout().perform(click()); |
||||
Gesture found = touch.await(500, TimeUnit.MILLISECONDS); |
||||
assertNull(found); |
||||
} |
||||
|
||||
@Test |
||||
public void testLongTap() { |
||||
touch.listen(); |
||||
touch.start(); |
||||
GeneralClickAction a = new GeneralClickAction( |
||||
Tap.LONG, GeneralLocation.CENTER, Press.FINGER, |
||||
InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY); |
||||
onLayout().perform(a); |
||||
Gesture found = touch.await(500, TimeUnit.MILLISECONDS); |
||||
assertEquals(found, Gesture.LONG_TAP); |
||||
Size size = rule.getActivity().getContentSize(); |
||||
assertEquals(layout.getPoints()[0].x, (size.getWidth() / 2f), 1f); |
||||
assertEquals(layout.getPoints()[0].y, (size.getHeight() / 2f), 1f); |
||||
} |
||||
} |
@ -0,0 +1,75 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.app.Activity; |
||||
import android.app.KeyguardManager; |
||||
import android.content.Context; |
||||
import android.graphics.Point; |
||||
import android.os.Bundle; |
||||
import android.os.PersistableBundle; |
||||
import android.support.annotation.Nullable; |
||||
import android.view.Gravity; |
||||
import android.view.View; |
||||
import android.view.ViewGroup; |
||||
import android.view.WindowManager; |
||||
import android.widget.FrameLayout; |
||||
|
||||
import static android.view.ViewGroup.LayoutParams.*; |
||||
|
||||
public class TestActivity extends Activity { |
||||
|
||||
private ViewGroup content; |
||||
private Size contentSize = new Size(1000, 1000); |
||||
|
||||
@Override |
||||
public void onCreate(@Nullable Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
wakeScreen(); |
||||
|
||||
// Match parent decor view.
|
||||
FrameLayout root = new FrameLayout(this); |
||||
root.setKeepScreenOn(true); |
||||
root.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); |
||||
|
||||
// Inner content view with fixed size.
|
||||
// We want it to be fully visible or expresso will crash.
|
||||
Point size = new Point(); |
||||
getWindowManager().getDefaultDisplay().getSize(size); |
||||
int width = Math.min(size.x, size.y); |
||||
int height = Math.min(size.x, size.y); |
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( |
||||
width, height, Gravity.CENTER); |
||||
content = new FrameLayout(this); |
||||
content.setLayoutParams(params); |
||||
contentSize = new Size(width, height); |
||||
|
||||
// Add.
|
||||
root.addView(content); |
||||
setContentView(root); |
||||
} |
||||
|
||||
public void wakeScreen() { |
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
||||
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
||||
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON); |
||||
} |
||||
|
||||
public Size getContentSize() { |
||||
return contentSize; |
||||
} |
||||
|
||||
public ViewGroup getContentView() { |
||||
return content; |
||||
} |
||||
|
||||
public void inflate(View child) { |
||||
inflate(child, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); |
||||
} |
||||
|
||||
public void inflate(View child, ViewGroup.LayoutParams params) { |
||||
content.addView(child, params); |
||||
content.requestLayout(); |
||||
} |
||||
} |
@ -0,0 +1,43 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
|
||||
import android.content.Context; |
||||
import android.support.test.filters.SmallTest; |
||||
import android.support.test.runner.AndroidJUnit4; |
||||
import android.view.ViewGroup; |
||||
|
||||
import org.junit.runner.RunWith; |
||||
|
||||
@RunWith(AndroidJUnit4.class) |
||||
@SmallTest |
||||
public class TextureViewPreviewTest extends PreviewTest { |
||||
|
||||
@Override |
||||
protected Preview createPreview(Context context, ViewGroup parent, Preview.SurfaceCallback callback) { |
||||
return new TextureViewPreview(context, parent, callback); |
||||
} |
||||
|
||||
@Override |
||||
protected void ensureAvailable() { |
||||
if (isHardwareAccelerated()) { |
||||
super.ensureAvailable(); |
||||
} else { |
||||
preview.onSurfaceAvailable( |
||||
surfaceSize.getWidth(), |
||||
surfaceSize.getHeight()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void ensureDestroyed() { |
||||
super.ensureDestroyed(); |
||||
if (!isHardwareAccelerated()) { |
||||
// Ensure it is called.
|
||||
preview.onSurfaceDestroyed(); |
||||
} |
||||
} |
||||
|
||||
private boolean isHardwareAccelerated() { |
||||
return preview.getView().isHardwareAccelerated(); |
||||
} |
||||
} |
@ -0,0 +1,72 @@ |
||||
package com.otaliastudios.cameraview; |
||||
|
||||
import android.support.annotation.NonNull; |
||||
|
||||
import java.util.concurrent.CountDownLatch; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* A naive implementation of {@link java.util.concurrent.CountDownLatch} |
||||
* to help in testing. |
||||
*/ |
||||
class Task<T> { |
||||
|
||||
private CountDownLatch mLatch; |
||||
private T mResult; |
||||
private int mCount; |
||||
|
||||
Task() { |
||||
} |
||||
|
||||
private boolean listening() { |
||||
return mLatch != null; |
||||
} |
||||
|
||||
void listen() { |
||||
if (listening()) throw new RuntimeException("Should not happen."); |
||||
mResult = null; |
||||
mLatch = new CountDownLatch(1); |
||||
} |
||||
|
||||
void start() { |
||||
if (!listening()) mCount++; |
||||
} |
||||
|
||||
void end(T result) { |
||||
if (mCount > 0) { |
||||
mCount--; |
||||
return; |
||||
} |
||||
|
||||
if (listening()) { // Should be always true.
|
||||
mResult = result; |
||||
mLatch.countDown(); |
||||
} |
||||
} |
||||
|
||||
T await() { |
||||
try { |
||||
mLatch.await(); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
T result = mResult; |
||||
mResult = null; |
||||
mLatch = null; |
||||
return result; |
||||
} |
||||
|
||||
T await(long time, @NonNull TimeUnit unit) { |
||||
try { |
||||
mLatch.await(time, unit); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
T result = mResult; |
||||
mResult = null; |
||||
mLatch = null; |
||||
return result; |
||||
} |
||||
|
||||
|
||||
} |
Loading…
Reference in new issue