Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5feb7fe
add qnn instrumentation test
sheetalarkadam Oct 10, 2024
6f34b5f
add qnn browserstack
sheetalarkadam Oct 11, 2024
b75440a
skip emulator tests for qnn
sheetalarkadam Oct 11, 2024
71d4f60
skip emulator tests for qnn
sheetalarkadam Oct 11, 2024
ffea6f8
changes
sheetalarkadam Oct 12, 2024
34c1849
qnn changes,separate tests out
sheetalarkadam Oct 13, 2024
b80f5eb
pass qnn version
sheetalarkadam Oct 14, 2024
e87326c
qnn test conditional android test build
sheetalarkadam Oct 15, 2024
e8249b7
read qnn version from sdk yaml, cleanup
sheetalarkadam Oct 22, 2024
b3ffe4c
add comments
sheetalarkadam Oct 23, 2024
aa6bac0
update condtion
sheetalarkadam Oct 23, 2024
6887707
merge conflicts
sheetalarkadam Oct 23, 2024
fab730f
remove unused qnn files from test apk, indent fixes
sheetalarkadam Oct 23, 2024
572f3a2
use sdk version as stage variable and syntax fixes
sheetalarkadam Oct 23, 2024
43dea87
exclude libQnnDsp files
sheetalarkadam Oct 23, 2024
acca4ba
update comments, var names, formatting,simplify test
sheetalarkadam Oct 29, 2024
5adb321
simplify qnn_sdk_version passing, naming changes
sheetalarkadam Nov 1, 2024
939e180
use default qnn sdk parameter
sheetalarkadam Nov 1, 2024
efab87d
pipeline yaml syntax fixes
sheetalarkadam Nov 1, 2024
7c020bd
var quoting, simplify conditionals, add logs
sheetalarkadam Nov 5, 2024
d3d8d9f
Merge remote-tracking branch 'origin/main' into sak/add_android_qnn_test
sheetalarkadam Nov 6, 2024
a0e6230
update qnn sdk version
sheetalarkadam Nov 6, 2024
827a1bb
resolve conflicts
sheetalarkadam Nov 7, 2024
38e7264
resolve formatting issues
sheetalarkadam Nov 8, 2024
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
6 changes: 6 additions & 0 deletions java/src/test/android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ This android application is mainly aimed for testing:
- The [Gradle](https://gradle.org/) build system is required for building the APKs used to run [android instrumentation tests](https://source.android.com/compatibility/tests/development/instrumentation). Version 7.5 or newer is required.
The Gradle wrapper at `java/gradlew[.bat]` may be used.


### Building

Use the android's [build instructions](https://onnxruntime.ai/docs/build/android.html) with `--build_java` and `--android_run_emulator` option.

Please note that you may need to set the `--android_abi=x86_64` (the default option is `arm64-v8a`). This is because android instrumentation test is run on an android emulator which requires an abi of `x86_64`.

#### QNN Builds
For QNN builds, it isnecessary to set the `ADSP_LIBRARY_PATH` environment variable to the [native library directory](https://developer.android.com/reference/android/content/pm/ApplicationInfo#nativeLibraryDir) depending on the device. This ensures that any native libraries downloaded as dependencies such as QNN libraries are found by the application.

#### Build Output

The build will generate two apks which is required to run the test application in `$YOUR_BUILD_DIR/java/androidtest/android/app/build/outputs/apk`:
Expand All @@ -37,3 +41,5 @@ The build will generate two apks which is required to run the test application i
* `debug/app-debug.apk`

After running the build script, the two apks will be installed on `ort_android` emulator and it will automatically run the test application in an adb shell.

#### Manual Testing on QDC
31 changes: 31 additions & 0 deletions java/src/test/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
}

def minSdkVer = System.properties.get("minSdkVer")?:24
def qnnVersion = System.properties['qnnVersion']

android {
compileSdkVersion 32
Expand All @@ -16,6 +17,14 @@ android {
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

// Add BuildConfig field for qnnVersion
if (qnnVersion != null) {
buildConfigField "boolean", "IS_QNN_BUILD", "true"
}
else {
buildConfigField "boolean", "IS_QNN_BUILD", "false"
}
}

buildTypes {
Expand All @@ -31,6 +40,22 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
// Conditional packagingOptions for QNN builds only
if (qnnVersion != null) {
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
exclude 'lib/arm64-v8a/libQnnGpu.so'
}
}
sourceSets {
main {
if (System.properties['qnnVersion'] != null) {
manifest.srcFile 'src/main/AndroidManifestQnn.xml' // Use QNN manifest
}
}
}
namespace 'ai.onnxruntime.example.javavalidator'
}

Expand All @@ -49,4 +74,10 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test:rules:1.4.0'
androidTestImplementation 'com.microsoft.appcenter:espresso-test-extension:1.4'

// QNN only dependencies
if (qnnVersion != null) {
implementation "com.qualcomm.qti:qnn-runtime:$qnnVersion"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ class SimpleTest {

@Test
fun runSigmoidModelTestNNAPI() {
runSigmoidModelTestImpl(1, true)
runSigmoidModelTestImpl(1, useNNAPI = true)
}

@Test
fun runSigmoidModelTestQNN() {
runSigmoidModelTestImpl(1, useQNN = true)
}

@Throws(IOException::class)
Expand All @@ -54,14 +59,17 @@ class SimpleTest {
}

@Throws(OrtException::class, IOException::class)
fun runSigmoidModelTestImpl(intraOpNumThreads: Int, useNNAPI: Boolean = false) {
reportHelper.label("Start Running Test with intraOpNumThreads=$intraOpNumThreads, useNNAPI=$useNNAPI")
fun runSigmoidModelTestImpl(intraOpNumThreads: Int, useNNAPI: Boolean = false, useQNN: Boolean = false) {
reportHelper.label("Start Running Test with intraOpNumThreads=$intraOpNumThreads, useNNAPI=$useNNAPI, useQNN=$useQNN")
Log.println(Log.INFO, TAG, "Testing with intraOpNumThreads=$intraOpNumThreads")
Log.println(Log.INFO, TAG, "Testing with useNNAPI=$useNNAPI")
Log.println(Log.INFO, TAG, "Testing with useQNN=$useQNN")

val env = OrtEnvironment.getEnvironment(OrtLoggingLevel.ORT_LOGGING_LEVEL_VERBOSE)
env.use {
val opts = SessionOptions()
opts.setIntraOpNumThreads(intraOpNumThreads)

if (useNNAPI) {
if (OrtEnvironment.getAvailableProviders().contains(OrtProvider.NNAPI)) {
opts.addNnapi()
Expand All @@ -70,6 +78,18 @@ class SimpleTest {
return
}
}

if (useQNN) {
if (OrtEnvironment.getAvailableProviders().contains(OrtProvider.QNN)) {
// Since this is running in an Android environment, we use the .so library
val qnnLibrary = "libQnnHtp.so"
val providerOptions = Collections.singletonMap("backend_path", qnnLibrary)

opts.addQnn(providerOptions)
}
}


opts.use {
val session = env.createSession(readModel("sigmoid.ort"), opts)
session.use {
Expand All @@ -92,13 +112,14 @@ class SimpleTest {
output.use {
@Suppress("UNCHECKED_CAST")
val rawOutput = output[0].value as Array<Array<FloatArray>>
val precision = if (useQNN) 1e-3 else 1e-6
for (i in 0..2) {
for (j in 0..3) {
for (k in 0..4) {
Assert.assertEquals(
rawOutput[i][j][k],
expected[i][j][k],
1e-6.toFloat()
precision.toFloat()
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion java/src/test/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
</activity>
</application>

</manifest>
</manifest>
23 changes: 23 additions & 0 deletions java/src/test/android/app/src/main/AndroidManifestQnn.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.JavaValidator">
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<uses-native-library
android:name="libcdsprpc.so"
android:required="false" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package ai.onnxruntime.example.javavalidator

import android.os.Bundle
import android.system.Os
import androidx.appcompat.app.AppCompatActivity

/*Empty activity app mainly used for testing*/
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
if (BuildConfig.IS_QNN_BUILD) {
val adspLibraryPath = applicationContext.applicationInfo.nativeLibraryDir
// set the path variable to the native library directory
// so that the any native libraries downloaded as dependencies
// (like qnn libs) are found
Os.setenv("ADSP_LIBRARY_PATH", adspLibraryPath, true)
}
super.onCreate(savedInstanceState)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ parameters:
type: string
default: 'onnxruntime-android'

- name: QnnSDKVersion
displayName: QNN SDK version
type: string
default: '2.26.0.240828'

jobs:
- job: Final_AAR_Testing_Android_${{ parameters.job_name_suffix }}
workspace:
Expand Down Expand Up @@ -45,38 +50,56 @@ jobs:

- template: use-android-ndk.yml

- template: use-android-emulator.yml
parameters:
create: true
start: true

- script: |
set -e -x
mkdir android_test
cd android_test
cp -av $(Build.SourcesDirectory)/java/src/test/android ./
cd ./android
mkdir -p app/libs
cp $(Build.BinariesDirectory)/final-android-aar/${{parameters.packageName}}-$(OnnxRuntimeVersion).aar app/libs/onnxruntime-android.aar
$(Build.SourcesDirectory)/java/gradlew --no-daemon clean connectedDebugAndroidTest --stacktrace
displayName: Run E2E test using Emulator
set -e -x
mkdir -p android_test/android/app/libs
cd android_test/android
cp -av $(Build.SourcesDirectory)/java/src/test/android/* ./
cp $(Build.BinariesDirectory)/final-android-aar/${{parameters.packageName}}-$(OnnxRuntimeVersion).aar app/libs/onnxruntime-android.aar
displayName: Copy Android test files and AAR to android_test directory
workingDirectory: $(Build.BinariesDirectory)

- template: use-android-emulator.yml
parameters:
stop: true
# skip emulator tests for qnn package as there are no arm64-v8a emulators
- ${{ if not(contains(parameters.packageName, 'qnn')) }}:
- template: use-android-emulator.yml
parameters:
create: true
start: true

- script: |
set -e -x
cd android_test/android
$(Build.SourcesDirectory)/java/gradlew --no-daemon clean connectedDebugAndroidTest --stacktrace
displayName: Run E2E test using Emulator
workingDirectory: $(Build.BinariesDirectory)

- template: use-android-emulator.yml
parameters:
stop: true

# we run e2e tests on one older device (Pixel 3) and one newer device (Galaxy 23)
- script: |
set -e -x
cd android_test/android
# build apks for qnn package as they are not built in the emulator test step
if [[ ${{ parameters.packageName }} == *qnn* ]]; then
# qnn-runtime package is expected to be of the form qnn-runtime <major>.<minor>.<patch>
qnn_version=$(echo "${{ parameters.QnnSDKVersion }}" | cut -d'.' -f1-3)
$(Build.SourcesDirectory)/java/gradlew --no-daemon clean assembleDebug assembleAndroidTest -DqnnVersion=$qnn_version --stacktrace
fi

pip install requests
cd app/build/outputs/apk

python $(Build.SourcesDirectory)/tools/python/upload_and_run_browserstack_tests.py \
--test_platform espresso \
--app_apk_path "debug/app-debug.apk" \
--test_apk_path "androidTest/debug/app-debug-androidTest.apk" \
--devices "Samsung Galaxy S23-13.0" "Google Pixel 3-9.0"
--devices "Samsung Galaxy S23-13.0" "Google Pixel 3-9.0" \
--build_tag "${{ parameters.packageName }}"

displayName: Run E2E tests using Browserstack
workingDirectory: $(Build.BinariesDirectory)/android_test/android/app/build/outputs/apk
workingDirectory: $(Build.BinariesDirectory)
timeoutInMinutes: 15
env:
BROWSERSTACK_ID: $(browserstack_username)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ jobs:

# Mount qnn volume if building qnn android package
if [[ ${{ parameters.packageName }} == *qnn* ]]; then
echo $(QnnSDKVersionYaml)
QNN_VOLUME="--volume $(QnnSDKRootDir):/qnn_home"
USE_QNN="1"
else
Expand Down
14 changes: 12 additions & 2 deletions tools/ci_build/github/azure-pipelines/templates/c-api-cpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ parameters:
type: string
default: '0'

- name: QnnSDKVersion
type: string
default: '2.26.0.240828'

stages:
- template: linux-cpu-packaging-pipeline.yml
parameters:
Expand Down Expand Up @@ -88,10 +92,16 @@ stages:
buildSettings: '$(Build.SourcesDirectory)/tools/ci_build/github/android/default_qnn_aar_build_settings.json'
artifactName: 'onnxruntime-android-qnn-aar'
job_name_suffix: 'QNN'
publish_executables: '0'
publish_executables: '1'
enable_code_sign: ${{ parameters.DoEsrp }}
packageName: 'onnxruntime-android-qnn'
#TODO: Add test job for QNN Android AAR

- template: android-java-api-aar-test.yml
parameters:
artifactName: 'onnxruntime-android-qnn-aar'
job_name_suffix: 'QNN'
packageName: 'onnxruntime-android-qnn'
QnnSDKVersion: ${{ parameters.QnnSDKVersion }}

- stage: iOS_Full_xcframework
dependsOn: []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ steps:
echo $(QnnSDKRootDir)
displayName: 'Print QnnSDKRootDir after downloading QNN SDK'

- script: |
sdk_file="$(QnnSDKRootDir)/sdk.yaml"
qnn_sdk_version=$(grep '^version:' "$sdk_file" | head -n 1 | cut -d':' -f2 | xargs | cut -d'.' -f1-3)
if [[ -n "$qnn_sdk_version" ]]; then
echo "##vso[task.setvariable variable=QnnSDKVersionYaml]$qnn_sdk_version"
else
echo "Version not found in sdk.yaml."
exit 1
fi
displayName: Set QnnSDKVersionYaml based on sdk.yaml

- bash: |
echo "QnnSDKVersionYaml=$(QnnSDKVersionYaml)"
displayName: 'Print QnnSDKVersionYaml after extracting from sdk.yaml'

- script: |
azcopy cp --recursive 'https://lotusscus.blob.core.windows.net/models/qnnsdk/Qualcomm AI Hub Proprietary License.pdf' $(QnnSDKRootDir)
displayName: 'Download Qualcomm AI Hub license'
Expand Down
14 changes: 13 additions & 1 deletion tools/python/upload_and_run_browserstack_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ def upload_apk_parse_json(post_url, apk_path, id, token):
return response_to_json(response)


def browserstack_build_request(devices, app_url, test_suite_url, test_platform, id, token):
def browserstack_build_request(devices, app_url, test_suite_url, test_platform, id, token, project, build_tag):
headers = {}

json_data = {
"devices": devices,
"app": app_url,
"testSuite": test_suite_url,
"project": project,
"buildTag": build_tag,
"deviceLogs": True,
}

build_response = requests.post(
Expand Down Expand Up @@ -106,6 +109,13 @@ def build_query_loop(build_id, test_platform, id, token):
required=True,
)

parser.add_argument(
"--project",
type=str,
help="Identifier to logically group multiple builds together",
default="ONNXRuntime tests",
)
parser.add_argument("--build_tag", type=str, help="Identifier to tag the build with a unique name", default="")
args = parser.parse_args()

try:
Expand Down Expand Up @@ -140,6 +150,8 @@ def build_query_loop(build_id, test_platform, id, token):
args.test_platform,
browserstack_id,
browserstack_token,
args.project,
args.build_tag,
)

# Get build status until the tests are no longer running
Expand Down
Loading