Advertisement
Guest User

Untitled

a guest
Jul 1st, 2016
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.92 KB | None | 0 0
  1. package com.tsview.robolectric;
  2.  
  3. import android.app.Activity;
  4. import android.app.Application;
  5. import android.content.Intent;
  6. import android.os.Build;
  7. import android.os.Bundle;
  8. import android.support.annotation.NonNull;
  9. import android.support.annotation.Nullable;
  10. import android.support.v4.app.Fragment;
  11. import android.support.v4.app.FragmentActivity;
  12. import android.widget.LinearLayout;
  13.  
  14. import org.junit.runner.RunWith;
  15. import org.robolectric.Robolectric;
  16. import org.robolectric.RobolectricTestRunner;
  17. import org.robolectric.RuntimeEnvironment;
  18. import org.robolectric.annotation.Config;
  19. import org.robolectric.util.ActivityController;
  20.  
  21. import java.util.concurrent.TimeUnit;
  22. import java.util.concurrent.atomic.AtomicInteger;
  23.  
  24. /** Test holder that simplify robolectric UI tests execution. */
  25. @Config(sdk = Build.VERSION_CODES.LOLLIPOP, manifest = "src/main/AndroidManifest.xml")
  26. @RunWith(RobolectricTestRunner.class)
  27. public abstract class RobolectricTestsHelper {
  28. /** New line delimiter. */
  29. public final static String NEW_LINE = "\r\n";
  30.  
  31. /** Trace activity/fragment lifecycle states. Default: false. */
  32. public static final String OPTION_LIFECYCLE = "trace-lifecycle";
  33. /** Should log messages be with timestamp. By default: true; */
  34. public final static String OPTION_TIMESTAMP = "use-timestamp";
  35.  
  36. /** Async Test synchronization instance. */
  37. public final AtomicInteger SYNC_COUNTER = new AtomicInteger();
  38. /** Dynamic options/configurations that influence on tests output. */
  39. public final Map<String, Boolean> Options = new HashMap<>();
  40. /** Standard Output Logger. Helps to save some useful results of tests as a part of execution. */
  41. private final StringBuilder mLog = new StringBuilder(64 * 1024).append(NEW_LINE);
  42.  
  43. /**
  44. * Perform full lifecycle emulation for activity. When Activity is in visible state is possible to execute some
  45. * additional actions.
  46. *
  47. * @param onRecreate provide instance if you want to test recreation of the activity, otherwise NULL.
  48. */
  49. public <T extends Activity> ActivityController<T> fullLifecycle(
  50. @NonNull ActivityController<T> controller,
  51. @Nullable final Runnable onRestart,
  52. @Nullable final Runnable onResume,
  53. @Nullable final Runnable onVisible,
  54. @Nullable final RecreateRunnable<T> onRecreate) {
  55. Bundle savedInstanceState = null;
  56. final boolean doTrace = option(OPTION_LIFECYCLE, false);
  57.  
  58. // do recreate only if defined callback
  59. int recreateLoops = (null != onRecreate) ? 1 : 0;
  60. do {
  61. if (doTrace) trace("state - onCreate : " + recreateLoops);
  62. controller.create(savedInstanceState);
  63.  
  64. // CYCLE #1: emulate activity restart
  65. int lifeLoops = 1;
  66. do {
  67. if (doTrace) trace("state - onStart : " + lifeLoops);
  68. controller.start();
  69.  
  70. if (null != savedInstanceState) {
  71. controller.restoreInstanceState(savedInstanceState);
  72.  
  73. controller.postCreate(savedInstanceState);
  74. }
  75.  
  76. // CYCLE #1.1: emulate show/hide
  77. int loops = 1;
  78. do {
  79. if (doTrace) trace("state - onResume : " + loops);
  80. if (null != onResume) onResume.run();
  81.  
  82. controller.resume(); // --> onPostResume()
  83.  
  84. // TODO: onAttachedToWindow()
  85.  
  86. controller.visible(); // --> onCreateOptionsMenu(), onUserInteraction()
  87.  
  88. if (null != onVisible) onVisible.run();
  89.  
  90. controller.userLeaving();
  91.  
  92. controller.pause();
  93. if (doTrace) trace("state - onPause");
  94.  
  95. loops--;
  96. } while (loops >= 0);
  97.  
  98. // CHECK-ME: robolectric call it before #pause()
  99. controller.saveInstanceState(savedInstanceState = new Bundle());
  100.  
  101. controller.stop();
  102. if (doTrace) trace("state - onStop");
  103.  
  104. // TODO: onRetainNonConfigurationInstance() --> controller.get().onRetainNonConfigurationInstance();
  105.  
  106. // go-to onRestart() state
  107. if (lifeLoops > 0) {
  108. if (null != onRestart) onRestart.run();
  109.  
  110. // during restart we do not need the savedInstanceState, drop the instance
  111. savedInstanceState = null;
  112.  
  113. if (doTrace) trace("state - onRestart");
  114. controller.restart();
  115. }
  116.  
  117. lifeLoops--;
  118. } while (lifeLoops >= 0);
  119.  
  120. if (doTrace) trace("state - onDestroy");
  121. controller.destroy();
  122.  
  123. // save instance and recover it for additional lifecycle loop
  124. if (recreateLoops > 0) {
  125. if (null != onRecreate)
  126. controller = onRecreate.recreate(controller);
  127.  
  128. if (doTrace) trace("state - recreate : " + recreateLoops);
  129. controller.attach();
  130. }
  131.  
  132. recreateLoops--;
  133. } while (recreateLoops >= 0);
  134.  
  135. return controller; // can be a new instance due to re-create execution
  136. }
  137.  
  138. //region --> Fragment Lifecycle emulation
  139.  
  140. public static <T extends Fragment> ActivityController<FragmentHostingActivity> inject(
  141. @NonNull ActivityController<FragmentHostingActivity> controller,
  142. @Nullable Configure<T> configuration) {
  143.  
  144. controller.get().setConfiguration(configuration);
  145.  
  146. return controller;
  147. }
  148.  
  149. public static <T extends Fragment> ActivityController<FragmentHostingActivity> testFragment(final Class<T> clazz) {
  150. return testFragment(clazz, null);
  151. }
  152.  
  153. public static <T extends Fragment> ActivityController<FragmentHostingActivity> testFragment(
  154. @NonNull final Class<T> clazz, @Nullable final Bundle saved) {
  155. final Application application = RuntimeEnvironment.application;
  156. final Intent intent = new Intent(application, clazz);
  157.  
  158. if (null != saved) intent.putExtras(saved);
  159.  
  160. return testActivity(FragmentHostingActivity.class, intent);
  161. }
  162. //endregion
  163.  
  164. //region Options
  165.  
  166. /** Check the option. */
  167. public boolean option(final String name, final boolean $default) {
  168. if (Options.containsKey(name)) {
  169. return Options.get(name);
  170. }
  171.  
  172. return $default;
  173. }
  174.  
  175. /** Set option to a new value. */
  176. public void setOption(final String name, final boolean value) {
  177. Options.put(name, value);
  178. }
  179. //endregion
  180.  
  181. //region Standard Output
  182.  
  183. /** Get access to the logs memory storage directly. */
  184. @NonNull
  185. protected StringBuilder getRawLogger() {
  186. return mLog;
  187. }
  188.  
  189. public void log(final Level level, final String tag, final String msg) {
  190. final long last = option(OPTION_THREAD_TIMESTAMP, true) ? mLastMsg.get() : mTimestamp;
  191. final String timestamp = String.format(Locale.US, " (+%-5.3f ms)", millisFrom(last));
  192. final boolean doTimestamp = option(OPTION_TIMESTAMP, true);
  193.  
  194. mLog.append(level.toString().charAt(0)).append(" : ")
  195. .append(tag).append(" : ")
  196. .append(msg)
  197. .append(doTimestamp ? timestamp : "")
  198. .append(NEW_LINE);
  199.  
  200. // update timestamps
  201. mLastMsg.set(mTimestamp = System.nanoTime());
  202. }
  203.  
  204. /** trace single message without formatting. */
  205. public void trace(final String msg) {
  206. log(Level.INFO, "--", msg);
  207. }
  208.  
  209. /** trace with formatting. */
  210. public void trace(final String format, final Object... args) {
  211. final String message = (null == args) ? format : String.format(Locale.US, format, args);
  212.  
  213. log(Level.INFO, "--", message);
  214. }
  215.  
  216. /**
  217. * Convert nanoseconds to milliseconds with high accuracy.
  218. *
  219. * @param nanos nanoseconds to convert.
  220. * @return total milliseconds.
  221. */
  222. public static double toMillis(final long nanos) {
  223. return nanos / 1000.0 /* micros in 1 milli */ / 1000.0 /* nanos in 1 micro */;
  224. }
  225.  
  226. /** Get duration in milliseconds from start point till now. */
  227. public static double millisFrom(final long start) {
  228. return toMillis(System.nanoTime() - start);
  229. }
  230. //endregion
  231.  
  232. //region --> Nested Declarations
  233.  
  234. /** Implement interface for making possible testing of the activity re-creation. */
  235. public interface RecreateRunnable<T extends Activity> {
  236. ActivityController<T> recreate(final ActivityController<T> old);
  237. }
  238.  
  239. /** Configure instance of the fragment before it injecting into activity. */
  240. public interface Configure<T extends Fragment> {
  241. void configure(final T instance);
  242. }
  243.  
  244. /** Hosting activity that can be used for any fragment testing. */
  245. public static class FragmentHostingActivity extends FragmentActivity {
  246. /** Tag for easier finding of the fragment. */
  247. public static final String TAG_TEST_FRAGMENT = "TAG_TEST_FRAGMENT";
  248.  
  249. /** Cache of the instance. */
  250. private Fragment mInstance;
  251.  
  252. private Configure<Fragment> mConfiguration;
  253.  
  254. @Override
  255. protected void onCreate(@Nullable final Bundle saved) {
  256. super.onCreate(saved);
  257.  
  258. // first time call, otherwise restore from saved instance state should work
  259. if (null == saved) {
  260. // root view creation
  261. final LinearLayout view = new LinearLayout(this);
  262. view.setId(R.id.tag_view_holder);
  263. setContentView(view);
  264.  
  265. // fragment instance creation
  266. final Fragment fragment = getFragmentInstantiate(getIntent());
  267.  
  268. if (null != mConfiguration) {
  269. mConfiguration.configure(fragment);
  270. }
  271.  
  272. // integrate fragment
  273. getSupportFragmentManager()
  274. .beginTransaction()
  275. .replace(R.id.tag_view_holder, fragment, TAG_TEST_FRAGMENT)
  276. .commit();
  277. }
  278. }
  279.  
  280. /** Get instance of the Fragment from the fragment manager. Life instance. */
  281. @Nullable
  282. public Fragment getTestFragment() {
  283. return getSupportFragmentManager().findFragmentByTag(TAG_TEST_FRAGMENT);
  284. }
  285.  
  286. /** Create a new instance of the Fragment based on provided intent. */
  287. public Fragment getFragmentInstantiate(@NonNull final Intent intent) {
  288. if (null != mInstance)
  289. return mInstance;
  290.  
  291. final String className = intent.getComponent().getClassName();
  292. mInstance = Fragment.instantiate(this, className, intent.getExtras());
  293.  
  294. return mInstance;
  295. }
  296.  
  297. public void setFragmentInstantiate(final Fragment fragment) {
  298. mInstance = fragment;
  299. }
  300.  
  301. public <T extends Fragment> FragmentHostingActivity setConfiguration(final Configure<T> configuration) {
  302. mConfiguration = (Configure<Fragment>) ((Configure<?>) configuration);
  303.  
  304. return this;
  305. }
  306. }
  307. //endregion
  308. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement