Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CI: performance impact measurements #2171

Merged
merged 70 commits into from
Sep 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
466d695
test: appium-based android startup test (WIP)
vaind Jul 13, 2022
be814f5
chore: update appium test dependencies
vaind Jul 13, 2022
5de4fcc
test: add performance test apps
vaind Jul 13, 2022
3fe51dc
chore: perf test apps - add signing configs
vaind Jul 13, 2022
cca02ea
test: use appId
vaind Jul 13, 2022
3693a71
Format code
getsentry-bot Jul 13, 2022
93eaf86
refactor: switch appium tests to TS
vaind Jul 14, 2022
dcc86bf
chore: appium TS package cleanup
vaind Jul 14, 2022
08839af
chore: finish migration to nodejs appium
vaind Jul 14, 2022
7413898
chore: use simple-statistics in the appium test
vaind Jul 14, 2022
d7cb38a
chore: lints
vaind Jul 14, 2022
1f2973f
chore: update appium test to multi-app
vaind Jul 14, 2022
d1f36f7
chore: update appium test scripts
vaind Jul 14, 2022
31259c1
wip: upload apps to saucelabs
vaind Jul 15, 2022
99650f9
make gradlew executable in test apps
vaind Jul 15, 2022
b2f337f
fixup: saucelabs app upload
vaind Jul 15, 2022
d935121
refactor: use `appium:otherApps` instead of installApp
vaind Jul 18, 2022
4954245
ci: run appium perf test in CI
vaind Jul 18, 2022
f4850c6
test: add sentry to the startup test app
vaind Jul 18, 2022
891f3ee
Format code
getsentry-bot Jul 18, 2022
b4639f5
app-startup tests: run more iterations and filter outliers
vaind Jul 18, 2022
8b3a72d
test: compare startup times
vaind Jul 18, 2022
facd582
chore: docs
vaind Jul 18, 2022
98915c9
chore: include test-app-plain in the root gradle project
vaind Jul 18, 2022
88c8e74
test: tune logs and saucelabs info
vaind Jul 19, 2022
5a19276
test: print app size, add simple assertions, log formatting
vaind Jul 19, 2022
8c296ef
chore: move the new performance tests under sentry-android-integratio…
vaind Jul 20, 2022
a6ee6c0
move appium to appium-node
vaind Aug 1, 2022
732174d
refactor: move appium tests to kotlin (WIP)
vaind Aug 2, 2022
fdd1386
refactor: move appium tests to kotlin (WIP 2/*)
vaind Aug 3, 2022
6a814a7
refactor: move appium tests to kotlin (WIP 3/*)
vaind Aug 3, 2022
f557687
fix: SauceLabsClient - switch to ktor where file upload actually works
vaind Aug 3, 2022
b69b9ac
fix: SauceLabs otherApps format
vaind Aug 3, 2022
39d3ad0
refactor: remove node-based appium tests
vaind Aug 3, 2022
e49d9ca
Merge branch 'main' into ci/perf-tests
vaind Aug 3, 2022
e782e80
fix: make sure SauceLabsOnly is always true on CI
vaind Aug 3, 2022
0163d1c
avoid wildcard import to satisfy ktlintCheck
vaind Aug 3, 2022
a10f433
Format code
getsentry-bot Aug 3, 2022
973b876
ci: add saucelabs env vars
vaind Aug 3, 2022
fdfe700
chore: change test-app-* build.gradle to kotlin
vaind Aug 3, 2022
5793888
Merge remote-tracking branch 'origin/ci/perf-tests' into ci/perf-tests
vaind Aug 3, 2022
01ab854
chore: appium test - print test output logs
vaind Aug 3, 2022
bd43723
chore: appium test docs & minor cleanup
vaind Aug 4, 2022
47faa49
refactor: change android time collection to logcat-based
vaind Aug 4, 2022
acea7c9
chore: set measured app info as CI job outputs
vaind Aug 4, 2022
95d5c39
chore: add performance metrics to the PR comment
vaind Aug 4, 2022
7a62adf
Merge branch 'main' into ci/perf-tests
vaind Aug 4, 2022
86aa314
chore: remove wildcard import
vaind Aug 4, 2022
add51e8
fix: startup tests using logcat on SauceLabs
vaind Aug 4, 2022
6875b2d
chore: format GHA output StartTime
vaind Aug 4, 2022
6a3e607
chore: stick to Android 12 for the startup test
vaind Aug 4, 2022
7fc951f
fixup: number of runs for StartupTests on SauceLabs
vaind Aug 4, 2022
639a9e8
appium tests - ios support
vaind Aug 10, 2022
634cdb2
switch app integration test to use a github action
vaind Aug 11, 2022
316b5f3
chore: use comment-update logic in the metrics action
vaind Aug 11, 2022
153e2f2
fix ci yaml
vaind Aug 11, 2022
8d31406
update metrics GHA
vaind Aug 12, 2022
bbeb60d
Merge branch 'main' into ci/perf-tests
vaind Aug 12, 2022
5e9c63a
chore: update metrics GHA
vaind Aug 12, 2022
935e208
update perf test config
vaind Aug 12, 2022
9e63c21
ci: cache benchmark plain app
vaind Aug 14, 2022
f6723f6
switch metrics action to internal repo
vaind Aug 15, 2022
3f8cf60
Merge branch 'main' into ci/perf-tests
vaind Aug 17, 2022
a43974f
Merge branch 'main' into ci/perf-tests
vaind Sep 7, 2022
ffeb2a5
Merge branch 'main' into ci/perf-tests
marandaneto Sep 9, 2022
0e00c1a
chore: build only arm64 metrics test apps
vaind Sep 16, 2022
da971d2
chore: use Config.Android version constants in the metrics test apps
vaind Sep 16, 2022
059f3ea
fix: metrics test apps compat with old android
vaind Sep 16, 2022
f40bb1a
test: metrics binary size test range
vaind Sep 16, 2022
fde4c27
fix: metrics test apps old android compat
vaind Sep 16, 2022
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
42 changes: 37 additions & 5 deletions .github/workflows/integration-tests-benchmarks.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Integration Tests - Benchmarks"
name: 'Integration Tests - Benchmarks'
on:
schedule:
- cron: '0 0 * * *' # every night at midnight UTC
Expand All @@ -8,7 +8,7 @@ on:
- '**/sentry-android/**'
- '**/sentry-android-core/**'
- '**/sentry-android-ndk/**'
- '**/sentry-android-integration-tests/sentry-uitest-android-benchmark/**'
- '**/sentry-android-integration-tests/**'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd avoid using '**/sentry-android-integration-tests/**', since we don't want to run benchmarks if we only add a ui tests (the module sentry-android-integration-tests/sentry-uitest-android)

Copy link
Collaborator Author

@vaind vaind Aug 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess changes in uitest alone are so infrequent (to provide any benefit) that it doesn't make that much sense, considering we would need to explicitly white list the other 4 subdirectories instead?

- '**/.github/**'

jobs:
Expand All @@ -20,11 +20,11 @@ jobs:
- name: Git checkout
uses: actions/checkout@v2

- name: "Set up Java: 11"
- name: 'Set up Java: 11'
uses: actions/setup-java@v2
with:
java-version: "11"
distribution: "adopt"
java-version: '11'
distribution: 'adopt'

# Clean, build and release a test apk
- name: Make assembleBenchmarks
Expand All @@ -50,3 +50,35 @@ jobs:
sauce-username: ${{ secrets.SAUCE_USERNAME }}
sauce-access-key: ${{ secrets.SAUCE_ACCESS_KEY }}
config-file: .sauce/sentry-uitest-android-benchmark-lite.yml

app-metrics:
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v2

- name: 'Set up Java: 11'
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'

- uses: actions/cache@v3
id: app-plain-cache
with:
path: sentry-android-integration-tests/test-app-plain/build/outputs/apk/release/test-app-plain-release.apk
key: ${{ github.workflow }}-${{ github.job }}-appplain-${{ hashFiles('sentry-android-integration-tests/test-app-plain/**') }}

- name: Build Android app plain
if: steps.app-plain-cache.outputs['cache-hit'] != 'true'
run: ./gradlew :sentry-android-integration-tests:test-app-plain:assembleRelease

- name: Build Android app with Sentry
run: ./gradlew :sentry-android-integration-tests:test-app-sentry:assembleRelease

- name: Collect app metrics
uses: getsentry/action-app-sdk-overhead-metrics@v1
with:
config: sentry-android-integration-tests/metrics-test.yml
sauce-user: ${{ secrets.SAUCE_USERNAME }}
sauce-key: ${{ secrets.SAUCE_ACCESS_KEY }}
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ apiValidation {
"sentry-samples-netflix-dgs",
"sentry-uitest-android",
"sentry-uitest-android-benchmark",
"test-app-plain",
"test-app-sentry",
)
)
}
Expand Down
7 changes: 7 additions & 0 deletions sentry-android-integration-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Android performance-impact and integration tests

* [Sample app without sentry](./test-app-plain) created with Android Studio -> New Project -> Basic Activity
* [Same app, but with Sentry included](./test-app-sentry) - made part of the root project
* [App metrics test specification (yaml)](./metrics-test.yml)
* [Espresso-based benchmarks](./sentry-uitest-android-benchmark) - run within SauceLabs (see /.sauce/*.yml)
* [Espresso-based UI tests](./sentry-uitest-android) - run within SauceLabs (see /.sauce/*.yml)
16 changes: 16 additions & 0 deletions sentry-android-integration-tests/metrics-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apps:
- name: io.sentry.java.tests.perf.appplain
activity: MainActivity
path: ./test-app-plain/build/outputs/apk/release/test-app-plain-release.apk
- name: io.sentry.java.tests.perf.appsentry
activity: MainActivity
path: ./test-app-sentry/build/outputs/apk/release/test-app-sentry-release.apk

startupTimeTest:
runs: 50
diffMin: 0
diffMax: 150

binarySizeTest:
diffMin: 500 KiB
diffMax: 700 KiB
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
plugins {
id("com.android.application")
}

android {
compileSdk = Config.Android.compileSdkVersion

defaultConfig {
applicationId = "io.sentry.java.tests.perf.appplain"
minSdk = Config.Android.minSdkVersionNdk
targetSdk = Config.Android.targetSdkVersion
versionCode = 1
versionName = "1.0"
}

buildTypes {
getByName("release") {
isMinifyEnabled = true
signingConfig = signingConfigs.getByName("debug") // to be able to run release mode
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "benchmark-proguard-rules.pro")
ndk {
abiFilters.clear()
abiFilters.add("arm64-v8a")
}
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
buildFeatures {
viewBinding = true
}
signingConfigs {
getByName("debug") {
storeFile = rootProject.file("debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
}
}

variantFilter {
if (Config.Android.shouldSkipDebugVariant(buildType.name)) {
ignore = true
}
}
}

dependencies {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Sentry Android Core lib has the dependencies of:

    // lifecycle processor, session tracking
    implementation(Config.Libs.lifecycleProcess)
    implementation(Config.Libs.lifecycleCommonJava8)
    implementation(Config.Libs.androidxCore)

But most if not all Apps also have them as well, what if we add those dependencies in the plain test so they are not counted for the diff in size?

Copy link
Collaborator Author

@vaind vaind Sep 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, personally I'm not sure about that - you'd know better. Maybe we can try in a followup PR what's the difference?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good, so we unblock this PR.

implementation("androidx.appcompat:appcompat:1.3.0")
implementation("com.google.android.material:material:1.4.0")
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
implementation("androidx.navigation:navigation-fragment:2.3.5")
implementation("androidx.navigation:navigation-ui:2.3.5")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.sentry.java.tests.perf.appplain">

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.TestAppPlain"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.TestAppPlain.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.sentry.java.tests.perf.appplain;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;
import io.sentry.java.tests.perf.appplain.databinding.FragmentFirstBinding;

public class FirstFragment extends Fragment {

private FragmentFirstBinding binding;

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

binding = FragmentFirstBinding.inflate(inflater, container, false);
return binding.getRoot();
}

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

binding.buttonFirst.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
NavHostFragment.findNavController(FirstFragment.this)
.navigate(R.id.action_FirstFragment_to_SecondFragment);
}
});
}

@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.sentry.java.tests.perf.appplain;

import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.google.android.material.snackbar.Snackbar;
import io.sentry.java.tests.perf.appplain.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

private AppBarConfiguration appBarConfiguration;
private ActivityMainBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());

setSupportActionBar(binding.toolbar);

NavController navController =
Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);

binding.fab.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null)
.show();
}
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}

@Override
public boolean onSupportNavigateUp() {
NavController navController =
Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, appBarConfiguration)
|| super.onSupportNavigateUp();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.sentry.java.tests.perf.appplain;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;
import io.sentry.java.tests.perf.appplain.databinding.FragmentSecondBinding;

public class SecondFragment extends Fragment {

private FragmentSecondBinding binding;

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

binding = FragmentSecondBinding.inflate(inflater, container, false);
return binding.getRoot();
}

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

binding.buttonSecond.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
NavHostFragment.findNavController(SecondFragment.this)
.navigate(R.id.action_SecondFragment_to_FirstFragment);
}
});
}

@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
Loading