package net.cattaka.android.snippets.test;

import android.app.Activity;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.ViewActions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.v4.app.Fragment;
import android.test.InstrumentationTestCase;
import android.view.View;
import android.widget.Checkable;
import android.widget.TextView;

import org.hamcrest.Matcher;
import org.hamcrest.Matchers;

import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.hamcrest.CoreMatchers.allOf;

/**
 * Created by takao on 2014/12/12.
 */
public class TestUtils {
    public static <T> T pickValue(Object obj, String name) throws Exception {
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        return (T) field.get(obj);
    }

    public static void replaceValue(Object obj, String name, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static void performClick(final View view) throws Throwable {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                view.performClick();
            }
        });
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    }

    /**
     * Wait for given function returns true on UI thread
     */
    public static boolean waitForTrue(final BooleanFunc func, int timeout) throws Throwable {
        long l = SystemClock.elapsedRealtime();
        do {
            final AtomicBoolean flag = new AtomicBoolean();
            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
                @Override
                public void run() {
                    flag.set(func.run());
                }
            });
            if (flag.get()) {
                return true;
            }
            SystemClock.sleep(20);
        } while (SystemClock.elapsedRealtime() - l < timeout);

        return false;
    }

    public static boolean waitForActivityFinished(final Activity activity, int timeout) throws Throwable {
        return TestUtils.waitForTrue(new TestUtils.BooleanFunc() {
            @Override
            public boolean run() {
                return activity.isFinishing();
            }
        }, timeout);
    }

    public static boolean waitForFragmentResumed(final Fragment activity, int timeout) throws Throwable {
        return TestUtils.waitForTrue(new TestUtils.BooleanFunc() {
            @Override
            public boolean run() {
                return activity.isResumed();
            }
        }, timeout);
    }

    public static boolean waitForTextChanged(final TextView textView, final String expected, int timeout) throws Throwable {
        return TestUtils.waitForTrue(new TestUtils.BooleanFunc() {
            @Override
            public boolean run() {
                return String.valueOf(textView.getText()).equals(expected);
            }
        }, timeout);
    }

    public static boolean waitForCheckable(InstrumentationTestCase testCase, final Checkable view, final boolean checked, int timeout) throws Throwable {
        return TestUtils.waitForTrue(new TestUtils.BooleanFunc() {
            @Override
            public boolean run() {
                return view.isChecked() == checked;
            }
        }, timeout);
    }

    public static Matcher<View> withId(int id, Fragment fragment) {
        return allOf(ViewMatchers.withId(id), ViewMatchers.isDescendantOfA(Matchers.equalTo(fragment.getView())));
    }

    public static ViewAction scrollToEx() {
        return ViewActions.actionWithAssertions(new ScrollToExAction());
    }

    public interface BooleanFunc {
        boolean run();
    }
}

@@ -97,16 +97,57 @@
 mClock;%0A
+import android.support.annotation.IdRes;%0A
 import a
@@ -415,36 +415,108 @@
 oid.
-test.InstrumentationTestCase
+support.v7.widget.Toolbar;%0Aimport android.test.InstrumentationTestCase;%0Aimport android.view.MenuItem
 ;%0Aim
@@ -1656,24 +1656,525 @@
 c();%0A    %7D%0A%0A
+    public static void performClickMenuItem(final Toolbar.OnMenuItemClickListener listener, final Toolbar toolbar, @IdRes final int menuId) %7B%0A        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A                MenuItem item = toolbar.getMenu().findItem(menuId);%0A                listener.onMenuItemClick(item);%0A            %7D%0A        %7D);%0A        InstrumentationRegistry.getInstrumentation().waitForIdleSync();%0A    %7D%0A%0A
     /**%0A    
@@ -4558,32 +4558,384 @@
 tion());%0A    %7D%0A%0A
+    public static void showOverflowMenu(final Toolbar toolbar) %7B%0A        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A                toolbar.showOverflowMenu();%0A            %7D%0A        %7D);%0A        InstrumentationRegistry.getInstrumentation().waitForIdleSync();%0A    %7D%0A%0A
     public inter

Update TestUtils