From 7e13a3c9c1273f4e4c92aaa2a074b8d4c8b9aa3d Mon Sep 17 00:00:00 2001 From: Gaurav Shah Date: Thu, 28 Sep 2023 15:55:13 +0530 Subject: [PATCH 1/2] upgrade to newer versions of espresso and gradle and target Android 12 --- app/build.gradle | 18 +++++++++--------- app/src/main/AndroidManifest.xml | 4 +++- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6543b2f..e29aa22 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,13 +10,13 @@ buildscript { } android { - compileSdkVersion 30 + compileSdkVersion 31 dataBinding.enabled = true defaultConfig { applicationId "com.sample.browserstack.samplecalculator" minSdkVersion 15 - targetSdkVersion 30 + targetSdkVersion 31 versionCode 1 versionName "1.0" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' @@ -38,10 +38,10 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.0.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' - androidTestImplementation 'androidx.test:rules:1.1.1' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - testImplementation 'junit:junit:4.12' -} \ No newline at end of file + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'androidx.test:rules:1.3.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + testImplementation 'junit:junit:4.13' +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 69a6e9f..5e96d57 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,8 +9,10 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" + android:requestLegacyExternalStorage="true" android:theme="@style/AppTheme"> - + diff --git a/build.gradle b/build.gradle index 967757b..8e8d20c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.1' + classpath 'com.android.tools.build:gradle:7.0.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 14b3a0b..2178c96 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Nov 08 15:53:49 IST 2019 +#Mon Sep 04 18:31:18 IST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip From 238022929d1102c55946be0aa37452266c39fa78 Mon Sep 17 00:00:00 2001 From: avinash-bharti Date: Wed, 28 May 2025 12:49:01 +0530 Subject: [PATCH 2/2] Add java and c crash tests --- app/build.gradle | 13 +++++++++ .../samplecalculator/CrashJavaButtonTest.java | 27 ++++++++++++++++++ .../samplecalculator/CrashViaButtonTest.java | 28 +++++++++++++++++++ .../samplecalculator/EnsureJavaCrash.java | 27 ++++++++++++++++++ .../samplecalculator/EnsureSysCrash.java | 28 +++++++++++++++++++ .../samplecalculator/SysCrash.java | 28 +++++++++++++++++++ app/src/main/cpp/CMakeLists.txt | 9 ++++++ app/src/main/cpp/crashlib.cpp | 10 +++++++ .../samplecalculator/MainActivity.java | 26 +++++++++++++++++ app/src/main/res/layout/activity_main.xml | 19 +++++++++++++ 10 files changed, 215 insertions(+) create mode 100644 app/src/androidTest/java/com/sample/browserstack/samplecalculator/CrashJavaButtonTest.java create mode 100644 app/src/androidTest/java/com/sample/browserstack/samplecalculator/CrashViaButtonTest.java create mode 100644 app/src/androidTest/java/com/sample/browserstack/samplecalculator/EnsureJavaCrash.java create mode 100644 app/src/androidTest/java/com/sample/browserstack/samplecalculator/EnsureSysCrash.java create mode 100644 app/src/androidTest/java/com/sample/browserstack/samplecalculator/SysCrash.java create mode 100644 app/src/main/cpp/CMakeLists.txt create mode 100644 app/src/main/cpp/crashlib.cpp diff --git a/app/build.gradle b/app/build.gradle index e29aa22..0d2037e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,6 +20,19 @@ android { versionCode 1 versionName "1.0" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + // tell Gradle where our native CMake file lives + externalNativeBuild { + cmake { + // you can also restrict to specific ABIs here + abiFilters "arm64-v8a", "armeabi-v7a" + } + } + } + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + version "3.10.2" // or match your installed CMake version + } } buildTypes { release { diff --git a/app/src/androidTest/java/com/sample/browserstack/samplecalculator/CrashJavaButtonTest.java b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/CrashJavaButtonTest.java new file mode 100644 index 0000000..5344887 --- /dev/null +++ b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/CrashJavaButtonTest.java @@ -0,0 +1,27 @@ +package com.sample.browserstack.samplecalculator; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class CrashJavaButtonTest { + + @Rule + public ActivityTestRule activityRule = + new ActivityTestRule<>(MainActivity.class); + + @Test + public void crashJavaAppUnderTest() { + // this will throw the RuntimeException in Java, + // test harness will catch it and the process will record the stack-trace + onView(withId(R.id.buttonCrashJava)).perform(click()); + } +} diff --git a/app/src/androidTest/java/com/sample/browserstack/samplecalculator/CrashViaButtonTest.java b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/CrashViaButtonTest.java new file mode 100644 index 0000000..37dab7c --- /dev/null +++ b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/CrashViaButtonTest.java @@ -0,0 +1,28 @@ +package com.sample.browserstack.samplecalculator; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class CrashViaButtonTest { + + @Rule + public ActivityTestRule activityRule = + new ActivityTestRule<>(MainActivity.class); + + @Test + public void crashAppUnderTest() { + // Will invoke nativeCrash() and produce a real SIGSEGV + onView(withId(R.id.buttonCrash)).perform(click()); + + // (no further assertions—test will fail when the process dies) + } +} diff --git a/app/src/androidTest/java/com/sample/browserstack/samplecalculator/EnsureJavaCrash.java b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/EnsureJavaCrash.java new file mode 100644 index 0000000..962f48a --- /dev/null +++ b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/EnsureJavaCrash.java @@ -0,0 +1,27 @@ +package com.sample.browserstack.samplecalculator; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class EnsureJavaCrash { + + @Rule + public ActivityTestRule activityRule = + new ActivityTestRule<>(MainActivity.class); + + @Test + public void crashJavaAppUnderTest() { + // this will throw the RuntimeException in Java, + // test harness will catch it and the process will record the stack-trace + onView(withId(R.id.buttonCrashJava)).perform(click()); + } +} diff --git a/app/src/androidTest/java/com/sample/browserstack/samplecalculator/EnsureSysCrash.java b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/EnsureSysCrash.java new file mode 100644 index 0000000..f7582e1 --- /dev/null +++ b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/EnsureSysCrash.java @@ -0,0 +1,28 @@ +package com.sample.browserstack.samplecalculator; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class EnsureSysCrash { + + @Rule + public ActivityTestRule activityRule = + new ActivityTestRule<>(MainActivity.class); + + @Test + public void crashAppUnderTest() { + // Will invoke nativeCrash() and produce a real SIGSEGV + onView(withId(R.id.buttonCrash)).perform(click()); + + // (no further assertions—test will fail when the process dies) + } +} diff --git a/app/src/androidTest/java/com/sample/browserstack/samplecalculator/SysCrash.java b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/SysCrash.java new file mode 100644 index 0000000..bf3a452 --- /dev/null +++ b/app/src/androidTest/java/com/sample/browserstack/samplecalculator/SysCrash.java @@ -0,0 +1,28 @@ +package com.sample.browserstack.samplecalculator; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class SysCrash { + + @Rule + public ActivityTestRule activityRule = + new ActivityTestRule<>(MainActivity.class); + + @Test + public void crashAppUnderTest() { + // Will invoke nativeCrash() and produce a real SIGSEGV + onView(withId(R.id.buttonCrash)).perform(click()); + + // (no further assertions—test will fail when the process dies) + } +} diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..397d990 --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10.2) +project("crashlib") + +# Build a shared library called "crashlib" +add_library(crashlib SHARED crashlib.cpp) + +# Link against Android's log library if you ever want to log +find_library(log-lib log) +target_link_libraries(crashlib ${log-lib}) diff --git a/app/src/main/cpp/crashlib.cpp b/app/src/main/cpp/crashlib.cpp new file mode 100644 index 0000000..a34f26b --- /dev/null +++ b/app/src/main/cpp/crashlib.cpp @@ -0,0 +1,10 @@ +#include + +extern "C" +JNIEXPORT void JNICALL +Java_com_sample_browserstack_samplecalculator_MainActivity_nativeCrash( + JNIEnv* /* env */, jobject /* thiz */) { +// Force a write to address zero → produces SIGSEGV +volatile int* bad = nullptr; +*bad = 0; +} diff --git a/app/src/main/java/com/sample/browserstack/samplecalculator/MainActivity.java b/app/src/main/java/com/sample/browserstack/samplecalculator/MainActivity.java index e9e7331..8b73be9 100644 --- a/app/src/main/java/com/sample/browserstack/samplecalculator/MainActivity.java +++ b/app/src/main/java/com/sample/browserstack/samplecalculator/MainActivity.java @@ -11,6 +11,13 @@ public class MainActivity extends AppCompatActivity { + static { + // Load our native library at startup + System.loadLibrary("crashlib"); + } + // Native method declaration + private native void nativeCrash(); + private double firstNum = Double.NaN; private double secondNum; private boolean equalClicked = false; @@ -24,6 +31,25 @@ protected void onCreate(Bundle savedInstanceState) { binding = DataBindingUtil.setContentView(this, R.layout.activity_main); + + // Show the crash button and hook it into nativeCrash() + binding.buttonCrash.setVisibility(View.VISIBLE); + binding.buttonCrash.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + nativeCrash(); // <— this will SIGSEGV in native code + } + }); + + // 2) Java crash button + binding.buttonCrashJava.setVisibility(View.VISIBLE); + binding.buttonCrashJava.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + throw new RuntimeException("💥 Deliberate Java crash from test button"); + } + }); + binding.editText.setText(""); binding.buttonClear.setOnClickListener(new View.OnClickListener() { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 6f36af3..f201ef0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -29,6 +29,25 @@ android:maxLines="2" android:textSize="20sp" /> +