CI improvements (#695)

pull/697/head
Mattia Iavarone 5 years ago committed by GitHub
parent bb3b23e69a
commit 859904ec6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      .github/workflows/build.yml
  2. 6
      build.gradle
  3. 7
      cameraview/build.gradle
  4. 17
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/DoNotRunOnTravis.java
  5. 4
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/Camera1IntegrationTest.java
  6. 5
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/Camera2IntegrationTest.java
  7. 7
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/gesture/PinchGestureFinderTest.java
  8. 7
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/gesture/ScrollGestureFinderTest.java
  9. 6
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/gesture/TapGestureFinderTest.java
  10. 5
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfilesTest.java
  11. 18
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/ImageHelperTest.java
  12. 103
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/markers/MarkerLayoutTest.java
  13. 18
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/runner/SdkExclude.java
  14. 46
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/runner/SdkExcludeFilter.java
  15. 2
      gradle/wrapper/gradle-wrapper.properties

@ -38,7 +38,9 @@ jobs:
runs-on: macOS-latest
strategy:
matrix:
EMULATOR_API: [22]
# TODO 29 fails due to Mockito issues, probably reproducible locally.
# 22, 23, 24, 25, 26, 27, 28 work - some of them, with SdkExclude restrictions.
EMULATOR_API: [22, 23, 24, 25, 26, 27, 28]
EMULATOR_ARCH: [x86_64]
steps:
- uses: actions/checkout@v1
@ -46,12 +48,12 @@ jobs:
with:
java-version: 1.8
- name: Execute emulator tests
uses: reactivecircus/android-emulator-runner@v1
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.EMULATOR_API }}
arch: ${{ matrix.EMULATOR_ARCH }}
disable-animations: true
headless: true
emulator-options: -no-snapshot -no-window -no-boot-anim -camera-back emulated -camera-front emulated -memory 2048
script: ./gradlew cameraview:connectedCheck
- name: Upload emulator tests artifact
uses: actions/upload-artifact@v1

@ -5,7 +5,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
}
@ -19,9 +19,9 @@ allprojects {
}
ext {
compileSdkVersion = 28
compileSdkVersion = 29
minSdkVersion = 15
targetSdkVersion = 28
targetSdkVersion = 29
}
task clean(type: Delete) {

@ -8,8 +8,6 @@ group = 'com.otaliastudios'
//region android dependencies
def isCI = System.getenv("TRAVIS")
android {
compileSdkVersion rootProject.ext.compileSdkVersion
@ -19,9 +17,7 @@ android {
versionCode 1
versionName project.version
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
if (isCI) {
testInstrumentationRunnerArgument "notAnnotation", "com.otaliastudios.cameraview.DoNotRunOnTravis"
}
testInstrumentationRunnerArgument "filter", "com.otaliastudios.cameraview.runner.SdkExcludeFilter"
}
buildTypes {
@ -109,6 +105,7 @@ install {
def bintrayUser
def bintrayKey
def isCI = System.getenv("TRAVIS")
if (isCI) {
bintrayUser = System.getenv("BINTRAY_USER")
bintrayKey = System.getenv("BINTRAY_KEY")

@ -1,17 +0,0 @@
package com.otaliastudios.cameraview;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Thanks to a testInstrumentationRunnerArgument in our build file, we will not
* execute these tests on Travis CI.
* The {@link RetentionPolicy#RUNTIME} is needed!
*
* https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner
*
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface DoNotRunOnTravis {
String because() default "";
}

@ -1,6 +1,5 @@
package com.otaliastudios.cameraview.engine;
import com.otaliastudios.cameraview.DoNotRunOnTravis;
import com.otaliastudios.cameraview.controls.Engine;
import org.junit.runner.RunWith;
@ -8,6 +7,7 @@ import org.junit.runner.RunWith;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.filters.RequiresDevice;
/**
* These tests work great on real devices, and are the only way to test actual CameraEngine
@ -17,7 +17,7 @@ import androidx.test.filters.LargeTest;
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
@DoNotRunOnTravis(because = "These do work but fail on CI emulators, due to bugs in the Camera1 emulated devices.")
@RequiresDevice
public class Camera1IntegrationTest extends CameraIntegrationTest {
@NonNull

@ -3,17 +3,16 @@ package com.otaliastudios.cameraview.engine;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import com.otaliastudios.cameraview.DoNotRunOnTravis;
import com.otaliastudios.cameraview.controls.Engine;
import com.otaliastudios.cameraview.engine.action.ActionHolder;
import com.otaliastudios.cameraview.engine.action.BaseAction;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.filters.RequiresDevice;
import java.util.concurrent.CountDownLatch;
@ -25,7 +24,7 @@ import java.util.concurrent.CountDownLatch;
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
@DoNotRunOnTravis(because = "These do work but fail on CI emulators.")
@RequiresDevice
public class Camera2IntegrationTest extends CameraIntegrationTest {
@NonNull

@ -6,6 +6,8 @@ import androidx.test.espresso.ViewAction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.runner.SdkExclude;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -13,6 +15,11 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* On API 26 these tests fail during Espresso's inRoot() - the window never gains focus.
* This might be due to a system popup or something similar.
*/
@SdkExclude(minSdkVersion = 26, maxSdkVersion = 26)
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PinchGestureFinderTest extends GestureFinderTest<PinchGestureFinder> {

@ -6,6 +6,8 @@ import androidx.test.espresso.ViewAction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.runner.SdkExclude;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -17,6 +19,11 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* On API 26 these tests fail during Espresso's inRoot() - the window never gains focus.
* This might be due to a system popup or something similar.
*/
@SdkExclude(minSdkVersion = 26, maxSdkVersion = 26)
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ScrollGestureFinderTest extends GestureFinderTest<ScrollGestureFinder> {

@ -11,6 +11,7 @@ import androidx.test.filters.SmallTest;
import android.view.InputDevice;
import android.view.MotionEvent;
import com.otaliastudios.cameraview.runner.SdkExclude;
import com.otaliastudios.cameraview.size.Size;
import org.junit.Test;
@ -19,6 +20,11 @@ import org.junit.runner.RunWith;
import static androidx.test.espresso.action.ViewActions.*;
import static org.junit.Assert.*;
/**
* On API 26 these tests fail during Espresso's inRoot() - the window never gains focus.
* This might be due to a system popup or something similar.
*/
@SdkExclude(minSdkVersion = 26, maxSdkVersion = 26)
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TapGestureFinderTest extends GestureFinderTest<TapGestureFinder> {

@ -8,6 +8,7 @@ import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.BaseTest;
import com.otaliastudios.cameraview.CameraUtils;
import com.otaliastudios.cameraview.runner.SdkExclude;
import com.otaliastudios.cameraview.size.Size;
import org.junit.Test;
@ -34,6 +35,10 @@ public class CamcorderProfilesTest extends BaseTest {
assertEquals(lowest.videoFrameHeight, invalid.videoFrameHeight);
}
/**
* For some reason this fails on emulator 26.
*/
@SdkExclude(minSdkVersion = 26, maxSdkVersion = 26)
@Test
public void testGet() {
String cameraId = getCameraId();

@ -12,33 +12,31 @@ import android.graphics.Rect;
import android.graphics.YuvImage;
import android.media.Image;
import android.media.ImageReader;
import android.opengl.GLES20;
import android.os.Handler;
import android.os.Looper;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.BaseTest;
import com.otaliastudios.cameraview.internal.egl.EglCore;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.internal.egl.EglWindowSurface;
import com.otaliastudios.cameraview.size.AspectRatio;
import com.otaliastudios.cameraview.size.Size;
import com.otaliastudios.cameraview.runner.SdkExclude;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Starting from API 29, surface.lockCanvas() sets the surface format to RGBA_8888:
* https://github.com/aosp-mirror/platform_frameworks_base/blob/android10-release/core/jni/android_view_Surface.cpp#L215-L217 .
* For this reason, acquireLatestImage crashes because we requested a different format.
*/
@SdkExclude(minSdkVersion = 29)
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ImageHelperTest extends BaseTest {

@ -9,6 +9,7 @@ import android.view.ViewGroup;
import com.otaliastudios.cameraview.BaseTest;
import com.otaliastudios.cameraview.TestActivity;
import com.otaliastudios.cameraview.runner.SdkExclude;
import org.junit.Assert;
import org.junit.Before;
@ -17,19 +18,19 @@ import org.junit.Test;
import org.mockito.Mockito;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.annotation.UiThreadTest;
import androidx.test.rule.ActivityTestRule;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Not clear why, but for some reason on API 28 the UiThreadTests here crash for an internal NPE
* in FrameLayout.onMeasure.
*/
@SdkExclude(minSdkVersion = 28, maxSdkVersion = 28)
@TargetApi(17)
public class MarkerLayoutTest extends BaseTest {
@ -54,58 +55,74 @@ public class MarkerLayoutTest extends BaseTest {
}
@Test
@UiThreadTest
public void testOnMarker_callsOnAttach() {
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, autoFocusMarker);
Mockito.verify(autoFocusMarker, times(1)).onAttach(
Mockito.any(Context.class),
Mockito.eq(markerLayout));
uiSync(new Runnable() {
@Override
public void run() {
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, autoFocusMarker);
Mockito.verify(autoFocusMarker, times(1)).onAttach(
Mockito.any(Context.class),
Mockito.eq(markerLayout));
}
});
}
@Test
@UiThreadTest
public void testOnMarker_addsView() {
Assert.assertEquals(markerLayout.getChildCount(), 0);
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, autoFocusMarker);
Assert.assertEquals(markerLayout.getChildCount(), 1);
uiSync(new Runnable() {
@Override
public void run() {
Assert.assertEquals(markerLayout.getChildCount(), 0);
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, autoFocusMarker);
Assert.assertEquals(markerLayout.getChildCount(), 1);
}
});
}
@Test
@UiThreadTest
public void testOnMarker_removesView() {
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, autoFocusMarker);
Assert.assertEquals(markerLayout.getChildCount(), 1);
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, autoFocusMarker);
Assert.assertEquals(markerLayout.getChildCount(), 1);
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, null);
Assert.assertEquals(markerLayout.getChildCount(), 0);
Mockito.verify(autoFocusMarker, times(2)).onAttach(
Mockito.any(Context.class),
Mockito.eq(markerLayout));
uiSync(new Runnable() {
@Override
public void run() {
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, autoFocusMarker);
Assert.assertEquals(markerLayout.getChildCount(), 1);
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, autoFocusMarker);
Assert.assertEquals(markerLayout.getChildCount(), 1);
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, null);
Assert.assertEquals(markerLayout.getChildCount(), 0);
Mockito.verify(autoFocusMarker, times(2)).onAttach(
Mockito.any(Context.class),
Mockito.eq(markerLayout));
}
});
}
@Test
@UiThreadTest
public void testOnEvent() {
final View mockView = spy(new View(getContext()));
// These fail, however it's not really needed.
// when(mockView.getWidth()).thenReturn(50);
// when(mockView.getHeight()).thenReturn(50);
AutoFocusMarker mockMarker = new AutoFocusMarker() {
public void onAutoFocusStart(@NonNull AutoFocusTrigger trigger, @NonNull PointF point) { }
public void onAutoFocusEnd(@NonNull AutoFocusTrigger trigger, boolean successful, @NonNull PointF point) { }
uiSync(new Runnable() {
@Override
public View onAttach(@NonNull Context context, @NonNull ViewGroup container) {
return mockView;
public void run() {
final View mockView = spy(new View(getContext()));
// These fail, however it's not really needed.
// when(mockView.getWidth()).thenReturn(50);
// when(mockView.getHeight()).thenReturn(50);
AutoFocusMarker mockMarker = new AutoFocusMarker() {
public void onAutoFocusStart(@NonNull AutoFocusTrigger trigger, @NonNull PointF point) { }
public void onAutoFocusEnd(@NonNull AutoFocusTrigger trigger, boolean successful, @NonNull PointF point) { }
@Override
public View onAttach(@NonNull Context context, @NonNull ViewGroup container) {
return mockView;
}
};
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, mockMarker);
reset(mockView);
markerLayout.onEvent(MarkerLayout.TYPE_AUTOFOCUS, new PointF[]{new PointF(0, 0)});
verify(mockView, times(1)).clearAnimation();
verify(mockView, times(1)).setTranslationX(anyFloat());
verify(mockView, times(1)).setTranslationY(anyFloat());
}
};
markerLayout.onMarker(MarkerLayout.TYPE_AUTOFOCUS, mockMarker);
reset(mockView);
markerLayout.onEvent(MarkerLayout.TYPE_AUTOFOCUS, new PointF[]{new PointF(0, 0)});
verify(mockView, times(1)).clearAnimation();
verify(mockView, times(1)).setTranslationX(anyFloat());
verify(mockView, times(1)).setTranslationY(anyFloat());
});
}
}

@ -0,0 +1,18 @@
package com.otaliastudios.cameraview.runner;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Like {@link androidx.test.filters.SdkSuppress}, but negative.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface SdkExclude {
/** The minimum API level to drop (inclusive) */
int minSdkVersion() default 1;
/** The maximum API level to drop (inclusive) */
int maxSdkVersion() default Integer.MAX_VALUE;
}

@ -0,0 +1,46 @@
package com.otaliastudios.cameraview.runner;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.test.internal.runner.filters.ParentFilter;
import org.junit.runner.Description;
/**
* Filter for {@link SdkExclude}, based on
* {@link androidx.test.internal.runner.TestRequestBuilder}'s SdkSuppressFilter.
*/
public class SdkExcludeFilter extends ParentFilter {
protected boolean evaluateTest(Description description) {
final SdkExclude sdkSuppress = getAnnotationForTest(description);
if (sdkSuppress != null) {
if (Build.VERSION.SDK_INT >= sdkSuppress.minSdkVersion()
&& Build.VERSION.SDK_INT <= sdkSuppress.maxSdkVersion()) {
return false; // exclude the test
}
return true; // run the test
}
return true; // no annotation, run the test
}
@Nullable
private SdkExclude getAnnotationForTest(Description description) {
final SdkExclude s = description.getAnnotation(SdkExclude.class);
if (s != null) {
return s;
}
final Class<?> testClass = description.getTestClass();
if (testClass != null) {
return testClass.getAnnotation(SdkExclude.class);
}
return null;
}
@Override
public String describe() {
return "Skip tests annotated with SdkExclude";
}
}

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

Loading…
Cancel
Save