Merge pull request #11 from Kottsone/main

Convert test-app into Android instrumented test
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a77871b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.iml
+.gradle
+/local.properties
+/.idea
+/.vscode
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
diff --git a/README.md b/README.md
index e631673..1bf44a2 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
 These functions execute multithreaded on the CPU. They take advantage of Neon/AdvSimd
 on Arm processors and SSE on Intel's.
 
-Compared to the RenderScript Intrinisics, this Toolkit is simpler to use and twice as fast
+Compared to the RenderScript Intrinsics, this Toolkit is simpler to use and twice as fast
 when executing on the CPU. However RenderScript Intrinsics allow more flexibility for
 the type of allocations supported. This toolkit does not support allocations of floats;
 most the functions support ByteArrays and Bitmaps.
diff --git a/build.gradle b/build.gradle
index b94928e..4cfb714 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,12 @@
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 buildscript {
-    ext.kotlin_version = "1.4.31"
+    ext.kotlin_version = "1.5.31"
     repositories {
         google()
         mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:4.2.0-beta06'
+        classpath 'com.android.tools.build:gradle:7.0.3'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 
         // NOTE: Do not place your application dependencies here; they belong
@@ -17,7 +17,7 @@
 allprojects {
     repositories {
         google()
-        jcenter()
+        mavenCentral()
     }
 }
 
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f2ff00a..6154b02 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 #Tue Mar 30 16:57:53 PDT 2021
 distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
diff --git a/local.properties b/local.properties
deleted file mode 100644
index e3d3799..0000000
--- a/local.properties
+++ /dev/null
@@ -1,10 +0,0 @@
-## This file is automatically generated by Android Studio.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file should *NOT* be checked into Version Control Systems,
-# as it contains information specific to your local configuration.
-#
-# Location of the SDK. This is only used by Gradle.
-# For customization when using a Version Control System, please read the
-# header note.
-sdk.dir=/home/jeanluc/Android/Sdk
\ No newline at end of file
diff --git a/renderscript-toolkit/build.gradle b/renderscript-toolkit/build.gradle
index bc50f90..7d62b0e 100644
--- a/renderscript-toolkit/build.gradle
+++ b/renderscript-toolkit/build.gradle
@@ -4,15 +4,15 @@
 }
 
 android {
-    compileSdkVersion 30
-    buildToolsVersion "30.0.3"
+    compileSdkVersion 31
+    buildToolsVersion "31.0.0"
 
     defaultConfig {
         minSdkVersion 16
-        targetSdkVersion 30
+        targetSdkVersion 31
         versionCode 1
         versionName "1.0"
-
+        
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         consumerProguardFiles "consumer-rules.pro"
         externalNativeBuild {
@@ -40,16 +40,10 @@
             path file('src/main/cpp/CMakeLists.txt')
         }
     }
-    //ndkVersion '22.0.7026061'
 }
 
 dependencies {
 
     implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
-    implementation 'androidx.core:core-ktx:1.3.2'
-    implementation 'androidx.appcompat:appcompat:1.2.0'
-    implementation 'com.google.android.material:material:1.3.0'
-    testImplementation 'junit:junit:4.+'
-    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+    implementation 'androidx.core:core-ktx:1.6.0'
 }
diff --git a/test-app/build.gradle b/test-app/build.gradle
index cb0da65..c7de909 100644
--- a/test-app/build.gradle
+++ b/test-app/build.gradle
@@ -4,13 +4,13 @@
 }
 
 android {
-    compileSdkVersion 30
-    buildToolsVersion "30.0.3"
+    compileSdkVersion 31
+    buildToolsVersion "31.0.0"
 
     defaultConfig {
         applicationId "com.google.android.renderscript_test"
-        minSdkVersion 16
-        targetSdkVersion 30
+        minSdkVersion 21
+        targetSdkVersion 31
         versionCode 1
         versionName "1.0"
 
@@ -35,10 +35,10 @@
 dependencies {
     implementation project(":renderscript-toolkit")
     implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
-    implementation 'androidx.core:core-ktx:1.3.2'
-    implementation 'androidx.appcompat:appcompat:1.2.0'
-    implementation 'com.google.android.material:material:1.3.0'
-    testImplementation 'junit:junit:4.+'
-    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+    implementation 'androidx.core:core-ktx:1.6.0'
+    implementation 'androidx.appcompat:appcompat:1.3.1'
+    implementation 'com.google.android.material:material:1.4.0'
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
 }
diff --git a/test-app/src/androidTest/java/com/google/android/renderscript_test/RenderScriptToolkitTest.kt b/test-app/src/androidTest/java/com/google/android/renderscript_test/RenderScriptToolkitTest.kt
new file mode 100644
index 0000000..0afa54b
--- /dev/null
+++ b/test-app/src/androidTest/java/com/google/android/renderscript_test/RenderScriptToolkitTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.content.Context
+import android.util.Log
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.LargeTest
+import org.junit.Assert.assertTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@LargeTest
+class RenderScriptToolkitTest {
+    @ExperimentalUnsignedTypes
+    @get:Rule
+    var activityScenarioRule = ActivityScenarioRule(MainActivity::class.java)
+
+    @Parameterized.Parameter(value = 0)
+    lateinit var intrinsic: Intrinsic
+
+    @ExperimentalUnsignedTypes
+    @Test
+    fun test() {
+        lateinit var thread: Thread
+        var success = false
+        activityScenarioRule.scenario.onActivity { activity ->
+            // Run the test in a new thread to avoid blocking the main thread.
+            thread = Thread{
+                success = testIntrinsic(activity, intrinsic, validate = true)
+            }.apply { start() }
+        }
+        thread.join()
+        assertTrue(success)
+    }
+
+    @ExperimentalUnsignedTypes
+    private fun testIntrinsic(context: Context, intrinsic: Intrinsic, validate: Boolean): Boolean {
+        val tester = Tester(context, validate)
+        val numberOfIterations = if (validate) 1 else 12
+        val timer = TimingTracker(numberOfIterations, numberOfIterationsToIgnore = 0)
+        val results = Array(numberOfIterations) { i ->
+            Log.i(TAG, "*** Starting iteration ${i + 1} of $numberOfIterations ****")
+            val success = tester.testOne(intrinsic, timer)
+            Log.i(TAG, if (success) timer.report() else "FAILED! FAILED! FAILED! FAILED!")
+            timer.nextIteration()
+            success
+        }
+        tester.destroy()
+        return results.all { it }
+    }
+
+    companion object {
+        private val TAG = "RenderScriptToolkitTest"
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun data(): List<Array<Any>> = Intrinsic.values().map { arrayOf(it) }
+    }
+}
diff --git a/test-app/src/main/AndroidManifest.xml b/test-app/src/main/AndroidManifest.xml
index 7dd5885..14fcf76 100644
--- a/test-app/src/main/AndroidManifest.xml
+++ b/test-app/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.google.android.renderscript_test">
 
+    <uses-sdk android:minSdkVersion="21" />
+
     <application
             android:allowBackup="true"
             android:label="@string/app_name"
@@ -9,7 +11,9 @@
             android:roundIcon="@mipmap/ic_launcher_round"
             android:supportsRtl="true"
             android:theme="@style/Theme.RenderscriptIntrinsicsReplacementToolkit">
-        <activity android:name=".MainActivity">
+        <activity
+            android:name=".MainActivity"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt b/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt
index 1025504..934456f 100644
--- a/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt
+++ b/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt
@@ -57,6 +57,18 @@
     */
 )
 
+enum class Intrinsic {
+    BLEND,
+    BLUR,
+    COLOR_MATRIX,
+    CONVOLVE,
+    HISTOGRAM,
+    LUT,
+    LUT3D,
+    RESIZE,
+    YUV_TO_RGB,
+}
+
 
 class Tester(context: Context, private val validate: Boolean) {
     private val renderscriptContext = RenderScript.create(context)
@@ -83,30 +95,20 @@
         renderscriptContext.destroy()
     }
 
+    // Test one single intrinsic. Return true on success and false on failure.
     @ExperimentalUnsignedTypes
-    fun testAll(timer: TimingTracker): String {
-        val tests  = listOf(
-            Pair("blend", ::testBlend),
-            Pair("blur", ::testBlur),
-            Pair("colorMatrix", ::testColorMatrix),
-            Pair("convolve", ::testConvolve),
-            Pair("histogram", ::testHistogram),
-            Pair("lut", ::testLut),
-            Pair("lut3d", ::testLut3d),
-            Pair("resize", ::testResize),
-            Pair("yuvToRgb", ::testYuvToRgb),
-        )
-        val results = Array(tests.size) { "" }
-        for (i in tests.indices) {
-            val (name, test) = tests[i]
-            println("Doing $name")
-            val success = test(timer)
-            results[i] = "$name " + if (success) "succeeded" else "FAILED! FAILED! FAILED! FAILED!"
-            println("      ${results[i]}")
-        }
-
-        return results.joinToString("\n")
-    }
+    fun testOne(intrinsic: Intrinsic, timer: TimingTracker) =
+        when(intrinsic) {
+            Intrinsic.BLEND -> ::testBlend
+            Intrinsic.BLUR -> ::testBlur
+            Intrinsic.COLOR_MATRIX -> ::testColorMatrix
+            Intrinsic.CONVOLVE -> ::testConvolve
+            Intrinsic.HISTOGRAM -> ::testHistogram
+            Intrinsic.LUT -> ::testLut
+            Intrinsic.LUT3D -> ::testLut3d
+            Intrinsic.RESIZE -> ::testResize
+            Intrinsic.YUV_TO_RGB -> ::testYuvToRgb
+        }.let { test -> test(timer) }
 
     @ExperimentalUnsignedTypes
     private fun testBlend(timer: TimingTracker): Boolean {
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/MainActivity.kt b/test-app/src/main/java/com/google/android/renderscript_test/MainActivity.kt
index 30eac4e..10ea5f7 100644
--- a/test-app/src/main/java/com/google/android/renderscript_test/MainActivity.kt
+++ b/test-app/src/main/java/com/google/android/renderscript_test/MainActivity.kt
@@ -17,14 +17,11 @@
 package com.google.android.renderscript_test
 
 import android.os.Bundle
-import android.text.method.ScrollingMovementMethod
-import android.widget.TextView
 import androidx.appcompat.app.AppCompatActivity
 
 
 @ExperimentalUnsignedTypes
 class MainActivity : AppCompatActivity() {
-
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)
@@ -38,42 +35,5 @@
         } catch (e: ReflectiveOperationException) {
             throw RuntimeException(e)
         }
-
-        startTests(this)
-    }
-
-    private fun startTests(activity: MainActivity) {
-        object : Thread() {
-            override fun run() {
-                val view = findViewById<TextView>(R.id.sample_text)
-                view.movementMethod = ScrollingMovementMethod()
-                val validate = true
-                val tester = Tester(activity, validate)
-                val numberOfIterations = if (validate) 1 else 12
-                val t = TimingTracker(numberOfIterations, 0)
-                for (i in 1..numberOfIterations) {
-                    val tag = "*** Starting iteration $i of $numberOfIterations ****\n"
-                    print(tag)
-                    runOnUiThread(Runnable { view.append(tag) })
-                    //startMethodTracing("myTracing")
-                    //startMethodTracingSampling("myTracing_sample", 8000000, 10)
-                    val result = tester.testAll(t)
-                    //stopMethodTracing()
-                    runOnUiThread(Runnable { view.append("$result\n\n${t.report()}\n") })
-                    t.nextIteration()
-                }
-                tester.destroy()
-                /*
-                while (i++ < 1000) {
-                    try {
-                        runOnUiThread(Runnable { btn.setText("#$i") })
-                        sleep(300)
-                    } catch (e: InterruptedException) {
-                        e.printStackTrace()
-                    }
-                }
-                 */
-            }
-        }.start()
     }
 }
diff --git a/test-app/src/main/res/layout/activity_main.xml b/test-app/src/main/res/layout/activity_main.xml
index ca83aaf..eb5586c 100644
--- a/test-app/src/main/res/layout/activity_main.xml
+++ b/test-app/src/main/res/layout/activity_main.xml
@@ -1,20 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        xmlns:app="http://schemas.android.com/apk/res-auto"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        tools:context=".MainActivity">
-
-    <TextView
-            android:id="@+id/sample_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text=""
-            android:scrollbars = "vertical"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintLeft_toLeftOf="parent"
-            app:layout_constraintRight_toRightOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity"/>
\ No newline at end of file