Skip to content

Espresso crash tests #22

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 22 additions & 9 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,29 @@ 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'
// 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 {
Expand All @@ -38,10 +51,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'
}
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'
}
Original file line number Diff line number Diff line change
@@ -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<MainActivity> 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());
}
}
Original file line number Diff line number Diff line change
@@ -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<MainActivity> 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)
}
}
Original file line number Diff line number Diff line change
@@ -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<MainActivity> 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());
}
}
Original file line number Diff line number Diff line change
@@ -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<MainActivity> 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)
}
}
Original file line number Diff line number Diff line change
@@ -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<MainActivity> 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)
}
}
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
<activity android:name=".MainActivity">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
9 changes: 9 additions & 0 deletions app/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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})
10 changes: 10 additions & 0 deletions app/src/main/cpp/crashlib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <jni.h>

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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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() {
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@
android:maxLines="2"
android:textSize="20sp" />

<Button
android:id="@+id/buttonCrash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CRASH"
android:visibility="gone"
android:layout_gravity="center"
android:background="@android:color/holo_red_light"/>

<Button
android:id="@+id/buttonCrashJava"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CRASH (java)"
android:visibility="gone"
android:layout_gravity="center_horizontal|top"
android:layout_marginTop="280dp"
android:background="@android:color/holo_orange_dark"/>

<Button
android:id="@+id/buttonSeven"
style="@style/Widget.AppCompat.Button.Borderless"
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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