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

NullPointerException at PopupWindow.getDecorViewLayoutParams #5345

Open
bfabiszewski opened this issue Nov 4, 2019 · 9 comments
Open

NullPointerException at PopupWindow.getDecorViewLayoutParams #5345

bfabiszewski opened this issue Nov 4, 2019 · 9 comments

Comments

@bfabiszewski
Copy link

Description

Running EditText setError() together with requestFocus() in onClick() handler causes NPE in getDecorViewLayoutParams(). It seems that decorView is null.

Interesting that running with @LooperMode(PAUSED) fixes the issue.

Steps to Reproduce

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.content_main);

        final EditText editText = findViewById(R.id.editText);
        final Button button = findViewById(R.id.button);

        button.setOnClickListener(v -> {
            editText.setError("Error");
            editText.requestFocus();
        });
    }

package net.fabiszewski.myapplication;

import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;

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.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.hasErrorText;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

    @Rule
    public ActivityScenarioRule<MainActivity> testRule = new ActivityScenarioRule<>(MainActivity.class);

    @Test
    public void editTextSetErrorTest() {

        // given
        ActivityScenario<MainActivity> scenario = testRule.getScenario();

        // when
        onView(withId(R.id.button)).perform(click());

        // then
        onView(withId(R.id.editText)).check(matches(hasErrorText("Error")));
    }

}
apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "net.fabiszewski.myapplication"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    testOptions {
        unitTests {
            includeAndroidResources = true
        }
    }
    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'org.robolectric:robolectric:4.3.1'
    testImplementation 'androidx.test.ext:junit:1.1.1'
    testImplementation 'androidx.test:runner:1.2.0'
    testImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
[Robolectric] net.fabiszewski.myapplication.MainActivityTest.editTextSetErrorTest: sdk=29; resources=BINARY
Called loadFromPath(/system/framework/framework-res.apk, true); mode=binary sdk=29
Cleaner.create(android.view.ThreadedRenderer@7c74e3de,android.graphics.HardwareRenderer$DestroyContextRunnable@2f76451b)

androidx.test.espresso.PerformException: Error performing 'single click - At Coordinates: 159, 88 and precision: 16, 16' on view 'with id: net.fabiszewski.myapplication:id/button'.

	at androidx.test.espresso.PerformException$Builder.build(PerformException.java:86)
	at androidx.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:87)
	at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:59)
	at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:322)
	at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:178)
	at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:119)
	at net.fabiszewski.myapplication.MainActivityTest.editTextSetErrorTest(MainActivityTest.java:32)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:546)
	at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:252)
	at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
	at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.NullPointerException
	at android.widget.PopupWindow.getDecorViewLayoutParams(PopupWindow.java:2348)
	at android.widget.PopupWindow.update(PopupWindow.java:2404)
	at android.widget.PopupWindow.update(PopupWindow.java:2382)
	at android.widget.Editor.setFrame(Editor.java:886)
	at android.widget.TextView.setFrame(TextView.java:7222)
	at android.view.View.layout(View.java:21924)
	at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
	at android.view.View.layout(View.java:21927)
	at android.view.ViewGroup.layout(ViewGroup.java:6260)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	at android.view.View.layout(View.java:21927)
	at android.view.ViewGroup.layout(ViewGroup.java:6260)
	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
	at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
	at android.view.View.layout(View.java:21927)
	at android.view.ViewGroup.layout(ViewGroup.java:6260)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	at android.view.View.layout(View.java:21927)
	at android.view.ViewGroup.layout(ViewGroup.java:6260)
	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
	at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
	at android.view.View.layout(View.java:21927)
	at android.view.ViewGroup.layout(ViewGroup.java:6260)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	at com.android.internal.policy.DecorView.onLayout(DecorView.java:779)
	at android.view.View.layout(View.java:21927)
	at android.view.ViewGroup.layout(ViewGroup.java:6260)
	at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3080)
	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2590)
	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721)
	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598)
	at android.os.Handler.handleCallback(Handler.java:883)
	at android.os.Handler.dispatchMessage(Handler.java:100)
	at org.robolectric.shadows.ShadowLegacyMessageQueue.dispatchMessage(ShadowLegacyMessageQueue.java:157)
	at org.robolectric.shadows.ShadowLegacyMessageQueue.access$200(ShadowLegacyMessageQueue.java:42)
	at org.robolectric.shadows.ShadowLegacyMessageQueue$1.run(ShadowLegacyMessageQueue.java:135)
	at org.robolectric.util.Scheduler$ScheduledRunnable.run(Scheduler.java:407)
	at org.robolectric.util.Scheduler.runOneTask(Scheduler.java:279)
	at org.robolectric.util.Scheduler.advanceTo(Scheduler.java:261)
	at org.robolectric.util.Scheduler.advanceBy(Scheduler.java:244)
	at org.robolectric.util.Scheduler.advanceBy(Scheduler.java:234)
	at org.robolectric.util.Scheduler.setIdleState(Scheduler.java:89)
	at org.robolectric.util.Scheduler.unPause(Scheduler.java:124)
	at org.robolectric.shadows.ShadowLegacyLooper.unPause(ShadowLegacyLooper.java:267)
	at org.robolectric.shadows.ShadowLegacyLooper.runPaused(ShadowLegacyLooper.java:324)
	at org.robolectric.shadows.ShadowViewGroup.addView(ShadowViewGroup.java:36)
	at android.view.ViewGroup.addView(ViewGroup.java)
	at android.view.ViewGroup.addView(ViewGroup.java:4892)
	at android.widget.PopupWindow.createDecorView(PopupWindow.java:1551)
	at android.widget.PopupWindow.preparePopup(PopupWindow.java:1498)
	at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1417)
	at android.widget.Editor.showError(Editor.java:583)
	at android.widget.Editor.setError(Editor.java:605)
	at android.widget.TextView.setError(TextView.java:7213)
	at android.widget.TextView.setError(TextView.java:7198)
	at net.fabiszewski.myapplication.MainActivity.lambda$onCreate$0(MainActivity.java:20)
	at android.view.View.performClick(View.java:7140)
	at android.view.View.performClickInternal(View.java:7117)
	at android.view.View.access$3500(View.java:801)
	at android.view.View$PerformClick.run(View.java:27351)
	at org.robolectric.util.Scheduler.runOrQueueRunnable(Scheduler.java:359)
	at org.robolectric.util.Scheduler.postDelayed(Scheduler.java:163)
	at org.robolectric.util.Scheduler.post(Scheduler.java:142)
	at org.robolectric.shadows.ShadowView.post(ShadowView.java:389)
	at android.view.View.post(View.java)
	at android.view.View.onTouchEvent(View.java:14823)
	at android.view.View$GeneratedProxy/182242344.onTouchEvent(Unknown Source)
	at org.robolectric.shadows.ShadowView.onTouchEvent(ShadowView.java:226)
	at android.view.View.onTouchEvent(View.java)
	at android.widget.TextView.onTouchEvent(TextView.java:10838)
	at android.view.View.dispatchTouchEvent(View.java:13430)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3082)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3082)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3082)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3082)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3082)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3082)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
	at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:465)
	at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1849)
	at android.app.Activity.dispatchTouchEvent(Activity.java:3993)
	at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
	at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:423)
	at org.robolectric.android.internal.LocalUiController.injectMotionEvent(LocalUiController.java:41)
	at androidx.test.espresso.base.UiControllerModule$EspressoUiControllerAdapter.injectMotionEvent(UiControllerModule.java:64)
	at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:162)
	at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:139)
	at androidx.test.espresso.action.Tap.sendSingleTap(Tap.java:170)
	at androidx.test.espresso.action.Tap.access$100(Tap.java:31)
	at androidx.test.espresso.action.Tap$1.sendTap(Tap.java:47)
	at androidx.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:137)
	at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:366)
	at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:255)
	at androidx.test.espresso.ViewInteraction.access$100(ViewInteraction.java:65)
	at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:158)
	at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:155)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at android.os.Handler.handleCallback(Handler.java:883)
	at android.os.Handler.dispatchMessage(Handler.java:100)
	at org.robolectric.shadows.ShadowLegacyMessageQueue.dispatchMessage(ShadowLegacyMessageQueue.java:157)
	at org.robolectric.shadows.ShadowLegacyMessageQueue.access$200(ShadowLegacyMessageQueue.java:42)
	at org.robolectric.shadows.ShadowLegacyMessageQueue$1.run(ShadowLegacyMessageQueue.java:135)
	at org.robolectric.util.Scheduler.runOrQueueRunnable(Scheduler.java:359)
	at org.robolectric.util.Scheduler.postDelayed(Scheduler.java:163)
	at org.robolectric.util.Scheduler.postDelayed(Scheduler.java:152)
	at org.robolectric.shadows.ShadowLegacyMessageQueue.enqueueMessage(ShadowLegacyMessageQueue.java:142)
	at android.os.MessageQueue.enqueueMessage(MessageQueue.java)
	at android.os.Handler.enqueueMessage(Handler.java:754)
	at android.os.Handler.sendMessageAtTime(Handler.java:703)
	at android.os.Handler.sendMessageDelayed(Handler.java:673)
	at android.os.Handler.post(Handler.java:403)
	at androidx.test.espresso.base.BaseLayerModule$1.execute(BaseLayerModule.java:92)
	at androidx.test.espresso.ViewInteraction.postAsynchronouslyOnUiThread(ViewInteraction.java:312)
	at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:166)
	at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:119)
	at net.fabiszewski.myapplication.MainActivityTest.editTextSetErrorTest(MainActivityTest.java:32)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	... 13 more


Process finished with exit code 255

Robolectric & Android Version

Robolectric 4.3
Android SDK 29

@realdadfish
Copy link
Contributor

Got the very same stacktrace today after I updated Google's Material Components library from 1.1.0-rc01 to 1.2.0-alpha04, my stacktrace is this:

java.lang.NullPointerException
	at android.widget.PopupWindow.getDecorViewLayoutParams(PopupWindow.java:2201)
	at android.widget.PopupWindow.update(PopupWindow.java:2257)
	at android.widget.PopupWindow.update(PopupWindow.java:2235)
	at android.widget.ListPopupWindow.show(ListPopupWindow.java:657)
	at android.widget.ListPopupWindow$GeneratedProxy/707354032.show(Unknown Source)
	at org.robolectric.shadows.ShadowListPopupWindow.show(ShadowListPopupWindow.java:22)
	at android.widget.ListPopupWindow.show(ListPopupWindow.java)
	at android.widget.AutoCompleteTextView.showDropDown(AutoCompleteTextView.java:1217)
	at android.widget.AutoCompleteTextView.setFrame(AutoCompleteTextView.java:1163)
	at android.view.View.layout(View.java:20669)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
	at android.view.View.layout(View.java:20672)
	at android.view.ViewGroup.layout(ViewGroup.java:6194)
	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)
	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)
	at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)
	at com.google.android.material.textfield.TextInputLayout.onLayout(TextInputLayout.java:3587)
	at android.view.View.layout(View.java:20672)
	at android.view.ViewGroup.layout(ViewGroup.java:6194)
	at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
	at android.view.View.layout(View.java:20672)
	at android.view.ViewGroup.layout(ViewGroup.java:6194)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
	at android.widget.ScrollView.onLayout(ScrollView.java:1552)
	at android.view.View.layout(View.java:20672)
	at android.view.ViewGroup.layout(ViewGroup.java:6194)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
	at android.view.View.layout(View.java:20672)
	at android.view.ViewGroup.layout(ViewGroup.java:6194)
	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)
	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)
	at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)
	at android.view.View.layout(View.java:20672)
	at android.view.ViewGroup.layout(ViewGroup.java:6194)
	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
	at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
	at com.android.internal.policy.DecorView.onLayout(DecorView.java:753)
	at android.view.View.layout(View.java:20672)
	at android.view.ViewGroup.layout(ViewGroup.java:6194)
	at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2792)
	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2319)
	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1460)
	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7183)
	at android.os.Handler.handleCallback(Handler.java:873)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at org.robolectric.shadows.ShadowLegacyMessageQueue.dispatchMessage(ShadowLegacyMessageQueue.java:157)
	at org.robolectric.shadows.ShadowLegacyMessageQueue.access$200(ShadowLegacyMessageQueue.java:42)
	at org.robolectric.shadows.ShadowLegacyMessageQueue$1.run(ShadowLegacyMessageQueue.java:135)
	at org.robolectric.util.Scheduler$ScheduledRunnable.run(Scheduler.java:407)
	at org.robolectric.util.Scheduler.runOneTask(Scheduler.java:279)
	at org.robolectric.util.Scheduler.advanceTo(Scheduler.java:261)
	at org.robolectric.util.Scheduler.advanceBy(Scheduler.java:244)
	at org.robolectric.util.Scheduler.advanceBy(Scheduler.java:234)
	at org.robolectric.util.Scheduler.setIdleState(Scheduler.java:89)
	at org.robolectric.util.Scheduler.unPause(Scheduler.java:124)
	at org.robolectric.shadows.ShadowLegacyLooper.unPause(ShadowLegacyLooper.java:267)
	at org.robolectric.shadows.ShadowLegacyLooper.runPaused(ShadowLegacyLooper.java:324)
	at org.robolectric.shadows.ShadowViewGroup.addView(ShadowViewGroup.java:36)
	at android.view.ViewGroup.addView(ViewGroup.java)
	at android.view.ViewGroup.addView(ViewGroup.java:4837)
	at android.widget.PopupWindow.createBackgroundView(PopupWindow.java:1388)
	at android.widget.PopupWindow.preparePopup(PopupWindow.java:1350)
	at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1277)
	at android.widget.ListPopupWindow.show(ListPopupWindow.java:696)
	at android.widget.ListPopupWindow$GeneratedProxy/707354032.show(Unknown Source)
	at org.robolectric.shadows.ShadowListPopupWindow.show(ShadowListPopupWindow.java:22)
	at android.widget.ListPopupWindow.show(ListPopupWindow.java)
	at android.widget.AutoCompleteTextView.showDropDown(AutoCompleteTextView.java:1217)
	at com.google.android.material.textfield.DropdownMenuEndIconDelegate.showHideDropdown(DropdownMenuEndIconDelegate.java:238)
	at com.google.android.material.textfield.DropdownMenuEndIconDelegate.access$400(DropdownMenuEndIconDelegate.java:65)
	at com.google.android.material.textfield.DropdownMenuEndIconDelegate$5.onTouch(DropdownMenuEndIconDelegate.java:351)
	at android.view.View.dispatchTouchEvent(View.java:12509)

@realdadfish
Copy link
Contributor

Problem seems to be a re-entrant call to AutoCompleteTextView.showDropDown(), for me at least.

PopupWindow.createBackgroundView() adds the contentView of the popup to a new instance of PopupBackgroundView. This addView call is intercepted by Robolectric and executed delayed (via shadowMainLooper().runPaused(...)). Because this call changes the layout, a new layout(l, t, r, b) pass is triggered on the AutoCompleteTextView, which in turn calls setFrame and - in case the popup is showing, which technically it isn't, but the flag for it was already switched - shows the popup again.

Now because the said flag is already switched, the code in PopupWindow skips the decorView initialization and crashes with the above shown NPE.

@realdadfish
Copy link
Contributor

Tracked this down, the issue appeared with Material Components 1.2.0-alpha03, because this is the first version where my test fails. It is green with Material Components 1.2.0-alpha02. Will futher investigate.

@realdadfish
Copy link
Contributor

My issue seems to be rooted in MDC, opened a bug there material-components/material-components-android#1027

@realdadfish
Copy link
Contributor

Ok, my workaround is to not touch / click the AutoCompleteTextView anymore, but open the popup programmatically, like this:

fun showDropDown(): ViewAction =
    object : ViewAction {
        override fun getDescription(): String = "Shows the dropdown menu of an AutoCompleteTextView"

        override fun getConstraints(): Matcher<View> = allOf(
            isEnabled(), isAssignableFrom(AutoCompleteTextView::class.java)
        )

        override fun perform(uiController: UiController, view: View) {
            val autoCompleteTextView = view as AutoCompleteTextView
            autoCompleteTextView.showDropDown()
            uiController.loopMainThreadUntilIdle()
        }
    }

@EricVanDerDijs
Copy link

EricVanDerDijs commented Oct 6, 2020

Hi, I'm having a very similar issue, I'm unable to use typeText() or click() on EditText throght espresso. here is my code:

    @Test
    public void on_date_click_the_date_picker_should_be_displayed() {
        FragmentScenario<ReportProblemFragment> scenario = FragmentScenario.launchInContainer(
                ReportProblemFragment.class, null, fragmentFactory);

        Espresso
                .onView(withId( R.id.et_problem ))
                .perform(typeText("Hi"));
    }

This produces:

androidx.test.espresso.PerformException: Error performing 'single click - At Coordinates: 89, 132 and precision: 16, 16' on view 'with id is <app.alpelo.android_courier.staging:id/et_problem_date>'.

	at androidx.test.espresso.PerformException$Builder.build(PerformException.java:5)
	at androidx.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:25)
	at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:36)
	at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:106)
	at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:43)
	at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:94)
	at app.alpelo.android_courier.fragments.ReportProblemFragmentTest.on_date_click_the_date_picker_should_be_displayed(ReportProblemFragmentTest.java:85)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:575)
	at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:263)
	at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
	at android.widget.Editor.access$300(Editor.java:144)
	at android.widget.Editor$InsertionHandleView.getCurrentCursorOffset(Editor.java:5167)
	at android.widget.Editor$HandleView.updateDrawable(Editor.java:4507)
	at android.widget.Editor$HandleView.__constructor__(Editor.java:4487)
	at android.widget.Editor$HandleView.<init>(Editor.java)
	at android.widget.Editor$InsertionHandleView.<init>(Editor.java)
	at android.widget.Editor$InsertionPointCursorController.getHandle(Editor.java:5691)
	at android.widget.Editor$InsertionPointCursorController.show(Editor.java:5666)
	at android.widget.Editor.onTouchUpEvent(Editor.java:2370)
	at android.widget.TextView.onTouchEvent(TextView.java:10115)
	at android.view.View.dispatchTouchEvent(View.java:12513)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
	at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:440)
	at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1830)
	at android.app.Activity.dispatchTouchEvent(Activity.java:3400)
	at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:398)
	at org.robolectric.android.internal.LocalUiController.injectMotionEvent(LocalUiController.java:41)
	at androidx.test.espresso.base.UiControllerModule$EspressoUiControllerAdapter.injectMotionEvent(UiControllerModule.java:8)
	at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:122)
	at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:117)
	at androidx.test.espresso.action.Tap.sendSingleTap(Tap.java:27)
	at androidx.test.espresso.action.Tap.access$100(Tap.java:21)
	at androidx.test.espresso.action.Tap$1.sendTap(Tap.java:3)
	at androidx.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:23)
	at androidx.test.espresso.action.TypeTextAction.perform(TypeTextAction.java:26)
	at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:16)
	at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:65)
	at androidx.test.espresso.ViewInteraction.access$100(ViewInteraction.java:15)
	at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:3)
	at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:2)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at android.os.Handler.handleCallback(Handler.java:873)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at org.robolectric.shadows.ShadowPausedLooper$IdlingRunnable.run(ShadowPausedLooper.java:302)
	at org.robolectric.shadows.ShadowPausedLooper.executeOnLooper(ShadowPausedLooper.java:267)
	at org.robolectric.shadows.ShadowPausedLooper.idle(ShadowPausedLooper.java:88)
	at org.robolectric.android.internal.LocalControlledLooper.drainMainThreadUntilIdle(LocalControlledLooper.java:15)
	at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:99)
	at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:43)
	at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:94)
	at app.alpelo.android_courier.fragments.ReportProblemFragmentTest.on_date_click_the_date_picker_should_be_displayed(ReportProblemFragmentTest.java:85)
	... 11 more

My Dependencies

    // testing
    testImplementation 'junit:junit:4.12'
    testImplementation 'androidx.test:core:1.3.0'
    testImplementation 'androidx.test.ext:junit:1.1.2'
    testImplementation 'org.robolectric:robolectric:4.5-alpha-1'
    testImplementation 'org.mockito:mockito-core:3.5.13'
    testImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    debugImplementation  'androidx.fragment:fragment-testing:1.2.5'
    // androidx
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "androidx.appcompat:appcompat:1.1.0"
    implementation "androidx.fragment:fragment:1.2.5"
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation "androidx.vectordrawable:vectordrawable-animated:1.1.0"
    implementation "com.google.android.material:material:1.1.0"
    implementation "androidx.browser:browser:1.2.0"
    implementation "androidx.legacy:legacy-support-v4:1.0.0"
    implementation "androidx.legacy:legacy-support-v4:1.0.0"
    implementation "androidx.cardview:cardview:1.0.0"

@pmoons
Copy link

pmoons commented Nov 3, 2021

Thank you @realdadfish for your solution. Finally got passed my espresso testing of my AutoCompleteTextView. FWIW, I had a slightly different stack trace than you, I am not using material components, and I am using Robolectric 4.6. @EricVanDerDijs if you are still experiencing problems with this, I'd suggest checking out @realdadfish 's answer. I had the same exact stack trace as you when I was trying to interact with the popup.

@joshallenit
Copy link

joshallenit commented Nov 30, 2021

+1, Full code: https://gist.github.com/joshallenit/04be926a7a31957817426d8d98d4af28

  @Test
  fun editText_whenClickedOnWithText_shouldNotCrash() {
    val scenario = launchFragmentInContainer<TestFragment>()
    scenario.onFragment {
      val underTest = it.view!!
      onView(withClassName(Matchers.equalTo(underTest::class.qualifiedName)))
        .perform(typeText("Hello "))
        .perform(typeText("c")) // CRASH
    }
  }

  class TestFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
      return EditText(requireContext())
    }
  }

Somehow the outer this reference is not being set in the InsertionHandleView inner class during construction. Not sure how this could happen, or how the byte code could be broken like this.

Caused by: java.lang.NullPointerException: Cannot read field "mTextView" because "x0" is null
	at android.widget.Editor.access$800(Editor.java:137)
	at android.widget.Editor$InsertionHandleView.getCurrentCursorOffset(Editor.java:4696)

@joshallenit
Copy link

joshallenit commented Dec 1, 2021

Here is a Shadow that works around it:
Full code https://gist.github.com/joshallenit/959213022791c86f1fcee40b04e8fc3c

@Implements(className="android.widget.Editor\$InsertionHandleView")
class ShadowInsertionHandleView: ShadowView() { {
  @RealObject
  private lateinit var realInsertionHandleView: Object

  @Suppress("unused", "ProtectedInFinal") // Used by Robolectric and protected recommended to limit api surface.
  @Implementation
  protected fun getCurrentCursorOffset(): Int {
    return try {
      Shadow.directlyOn(
        realInsertionHandleView,
        "android.widget.Editor\$InsertionHandleView",
        "getCurrentCursorOffset"
      )
    } catch (ex: Exception) {
      0
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants