1+ package com .microsoft .azure .mobile .sasquatch .activities ;
2+
3+ import android .content .Context ;
4+ import android .content .Intent ;
5+ import android .support .annotation .StringRes ;
6+ import android .support .test .espresso .Espresso ;
7+ import android .support .test .espresso .EspressoException ;
8+ import android .support .test .espresso .FailureHandler ;
9+ import android .support .test .espresso .ViewInteraction ;
10+ import android .support .test .espresso .matcher .BoundedMatcher ;
11+ import android .support .test .rule .ActivityTestRule ;
12+ import android .support .v4 .app .ActivityCompat ;
13+ import android .support .v4 .content .IntentCompat ;
14+ import android .view .View ;
15+
16+ import com .microsoft .azure .mobile .Constants ;
17+ import com .microsoft .azure .mobile .MobileCenter ;
18+ import com .microsoft .azure .mobile .crashes .Crashes ;
19+ import com .microsoft .azure .mobile .crashes .CrashesPrivateHelper ;
20+ import com .microsoft .azure .mobile .crashes .utils .ErrorLogHelper ;
21+ import com .microsoft .azure .mobile .sasquatch .R ;
22+ import com .microsoft .azure .mobile .utils .storage .StorageHelper ;
23+
24+ import org .hamcrest .Description ;
25+ import org .hamcrest .Matcher ;
26+ import org .junit .After ;
27+ import org .junit .Before ;
28+ import org .junit .Rule ;
29+ import org .junit .Test ;
30+
31+ import java .io .File ;
32+ import java .lang .reflect .Method ;
33+
34+ import static android .support .test .InstrumentationRegistry .getInstrumentation ;
35+ import static android .support .test .espresso .Espresso .onData ;
36+ import static android .support .test .espresso .Espresso .onView ;
37+ import static android .support .test .espresso .action .ViewActions .click ;
38+ import static android .support .test .espresso .assertion .ViewAssertions .matches ;
39+ import static android .support .test .espresso .matcher .RootMatchers .isDialog ;
40+ import static android .support .test .espresso .matcher .ViewMatchers .isDisplayed ;
41+ import static android .support .test .espresso .matcher .ViewMatchers .isRoot ;
42+ import static android .support .test .espresso .matcher .ViewMatchers .withChild ;
43+ import static android .support .test .espresso .matcher .ViewMatchers .withText ;
44+ import static com .microsoft .azure .mobile .sasquatch .activities .utils .EspressoUtils .CHECK_DELAY ;
45+ import static com .microsoft .azure .mobile .sasquatch .activities .utils .EspressoUtils .TOAST_DELAY ;
46+ import static com .microsoft .azure .mobile .sasquatch .activities .utils .EspressoUtils .onToast ;
47+ import static com .microsoft .azure .mobile .sasquatch .activities .utils .EspressoUtils .waitFor ;
48+ import static com .microsoft .azure .mobile .sasquatch .activities .utils .EspressoUtils .withContainsText ;
49+ import static org .hamcrest .Matchers .allOf ;
50+ import static org .hamcrest .Matchers .anyOf ;
51+ import static org .hamcrest .Matchers .instanceOf ;
52+ import static org .junit .Assert .assertTrue ;
53+
54+ @ SuppressWarnings ("unused" )
55+ public class CrashesTest {
56+
57+ @ Rule
58+ public ActivityTestRule <MainActivity > mActivityTestRule = new ActivityTestRule <>(MainActivity .class , true , false );
59+
60+ private Context mContext ;
61+
62+ @ Before
63+ public void setUp () throws Exception {
64+ mContext = getInstrumentation ().getTargetContext ();
65+
66+ /* Clear preferences. */
67+ StorageHelper .initialize (mContext );
68+ StorageHelper .PreferencesStorage .clear ();
69+
70+ /* Clear crashes. */
71+ Constants .loadFromContext (mContext );
72+ for (File logFile : ErrorLogHelper .getErrorStorageDirectory ().listFiles ()) {
73+ assertTrue (logFile .delete ());
74+ }
75+
76+ /* Launch main activity and go to setting page. Required to properly initialize. */
77+ mActivityTestRule .launchActivity (new Intent ());
78+
79+ /* Register IdlingResource */
80+ Espresso .registerIdlingResources (MainActivity .crashesIdlingResource );
81+ }
82+
83+ @ After
84+ public final void tearDown () {
85+
86+ /* Unregister IdlingResource */
87+ Espresso .unregisterIdlingResources (MainActivity .crashesIdlingResource );
88+ }
89+
90+ @ Test
91+ public void testCrashTest () throws InterruptedException {
92+ crashTest (R .string .title_test_crash );
93+ }
94+
95+ @ Test
96+ public void divideByZeroTest () throws InterruptedException {
97+ crashTest (R .string .title_crash_divide_by_0 );
98+ }
99+
100+ @ Test
101+ public void uiCrashTest () throws InterruptedException {
102+ crashTest (R .string .title_test_ui_crash );
103+ }
104+
105+ @ Test
106+ public void variableMessageTest () throws InterruptedException {
107+ crashTest (R .string .title_variable_message );
108+ }
109+
110+ /**
111+ * Crash and sending report test.
112+ * <p>
113+ * We can't truly restart application in tests, so some kind of crashes can't be tested by this method.
114+ * Out of memory or stack overflow - crash the test process;
115+ * UI states errors - problems with restart activity;
116+ * <p>
117+ * Also to avoid flakiness, please setup your test environment
118+ * (https://google.github.io/android-testing-support-library/docs/espresso/setup/index.html#setup-your-test-environment).
119+ * On your device, under Settings->Developer options disable the following 3 settings:
120+ * - Window animation scale
121+ * - Transition animation scale
122+ * - Animator duration scale
123+ *
124+ * @param titleId Title string resource to find list item.
125+ * @throws InterruptedException If the current thread is interrupted.
126+ */
127+ private void crashTest (@ StringRes int titleId ) throws InterruptedException {
128+
129+ /* Crash. */
130+ onView (allOf (
131+ withChild (withText (R .string .title_crashes )),
132+ withChild (withText (R .string .description_crashes ))))
133+ .perform (click ());
134+
135+ onCrash (titleId )
136+ .withFailureHandler (new CrashFailureHandler ())
137+ .perform (click ());
138+
139+ /* Check error report. */
140+ assertTrue (Crashes .hasCrashedInLastSession ());
141+
142+ /* Send report. */
143+ waitFor (onView (withText (R .string .crash_confirmation_dialog_send_button ))
144+ .inRoot (isDialog ()), 1000 )
145+ .perform (click ());
146+
147+ /* Check toasts. */
148+ waitFor (onToast (mActivityTestRule .getActivity (),
149+ withText (R .string .crash_before_sending )), CHECK_DELAY )
150+ .check (matches (isDisplayed ()));
151+ onView (isRoot ()).perform (waitFor (CHECK_DELAY ));
152+ waitFor (onToast (mActivityTestRule .getActivity (), anyOf (
153+ withContainsText (R .string .crash_sent_succeeded ),
154+ withText (R .string .crash_sent_failed ))), TOAST_DELAY )
155+ .check (matches (isDisplayed ()));
156+ onView (isRoot ()).perform (waitFor (TOAST_DELAY ));
157+ }
158+
159+ private ViewInteraction onCrash (@ StringRes int titleId ) {
160+ return onData (allOf (instanceOf (CrashActivity .Crash .class ), withCrashTitle (titleId )))
161+ .perform ();
162+ }
163+
164+ @ SuppressWarnings ("rawtypes" )
165+ private static Matcher <Object > withCrashTitle (@ StringRes final int titleId ) {
166+ return new BoundedMatcher <Object , CrashActivity .Crash >(CrashActivity .Crash .class ) {
167+
168+ @ Override
169+ public boolean matchesSafely (CrashActivity .Crash map ) {
170+ return map .title == titleId ;
171+ }
172+
173+ @ Override
174+ public void describeTo (Description description ) {
175+ description .appendText ("with item title from resource id: " );
176+ description .appendValue (titleId );
177+ }
178+ };
179+ }
180+
181+ private class CrashFailureHandler implements FailureHandler {
182+
183+ @ Override
184+ public void handle (Throwable error , Matcher <View > viewMatcher ) {
185+ Throwable uncaughtException = error instanceof EspressoException ? error .getCause () : error ;
186+
187+ /* Save exception. */
188+ CrashesPrivateHelper .saveUncaughtException (mContext .getMainLooper ().getThread (), uncaughtException );
189+
190+ /* Relaunch. */
191+ ActivityCompat .finishAffinity (mActivityTestRule .getActivity ());
192+ unsetInstance (MobileCenter .class );
193+ unsetInstance (Crashes .class );
194+ Intent intent = new Intent ();
195+ intent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK | IntentCompat .FLAG_ACTIVITY_CLEAR_TASK );
196+ mActivityTestRule .launchActivity (intent );
197+ }
198+
199+ @ SuppressWarnings ("unchecked" )
200+ private void unsetInstance (Class clazz ) {
201+ try {
202+ Method m = clazz .getDeclaredMethod ("unsetInstance" );
203+ m .setAccessible (true );
204+ m .invoke (null );
205+ } catch (Exception e ) {
206+ e .printStackTrace ();
207+ }
208+ }
209+ }
210+ }
0 commit comments