Home | History | Annotate | Download | only in servertransaction
      1 /*
      2  * Copyright 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.app.servertransaction;
     18 
     19 import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
     20 import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
     21 import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
     22 import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART;
     23 import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
     24 import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
     25 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
     26 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
     27 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
     28 
     29 import static junit.framework.Assert.assertEquals;
     30 
     31 import static org.junit.Assert.assertArrayEquals;
     32 import static org.mockito.ArgumentMatchers.any;
     33 import static org.mockito.ArgumentMatchers.eq;
     34 import static org.mockito.Mockito.inOrder;
     35 import static org.mockito.Mockito.mock;
     36 import static org.mockito.Mockito.spy;
     37 import static org.mockito.Mockito.times;
     38 import static org.mockito.Mockito.verify;
     39 import static org.mockito.Mockito.when;
     40 
     41 import android.app.ActivityThread.ActivityClientRecord;
     42 import android.app.ClientTransactionHandler;
     43 import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
     44 import android.os.IBinder;
     45 import android.os.Parcel;
     46 import android.os.Parcelable;
     47 import android.platform.test.annotations.Presubmit;
     48 import android.support.test.filters.SmallTest;
     49 import android.support.test.runner.AndroidJUnit4;
     50 
     51 import org.junit.Before;
     52 import org.junit.Test;
     53 import org.junit.runner.RunWith;
     54 import org.mockito.InOrder;
     55 
     56 import java.util.Arrays;
     57 import java.util.Collections;
     58 import java.util.List;
     59 import java.util.stream.Collectors;
     60 
     61 /** Test {@link TransactionExecutor} logic. */
     62 @RunWith(AndroidJUnit4.class)
     63 @SmallTest
     64 @Presubmit
     65 public class TransactionExecutorTests {
     66 
     67     private TransactionExecutor mExecutor;
     68     private TransactionExecutorHelper mExecutorHelper;
     69     private ClientTransactionHandler mTransactionHandler;
     70     private ActivityClientRecord mClientRecord;
     71 
     72     @Before
     73     public void setUp() throws Exception {
     74         mTransactionHandler = mock(ClientTransactionHandler.class);
     75 
     76         mClientRecord = new ActivityClientRecord();
     77         when(mTransactionHandler.getActivityClient(any())).thenReturn(mClientRecord);
     78 
     79         mExecutor = spy(new TransactionExecutor(mTransactionHandler));
     80         mExecutorHelper = new TransactionExecutorHelper();
     81     }
     82 
     83     @Test
     84     public void testLifecycleFromPreOnCreate() {
     85         mClientRecord.setState(PRE_ON_CREATE);
     86         assertArrayEquals(new int[] {}, path(PRE_ON_CREATE));
     87         assertArrayEquals(new int[] {ON_CREATE}, path(ON_CREATE));
     88         assertArrayEquals(new int[] {ON_CREATE, ON_START}, path(ON_START));
     89         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, path(ON_RESUME));
     90         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
     91         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
     92                 path(ON_STOP));
     93         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY},
     94                 path(ON_DESTROY));
     95     }
     96 
     97     @Test
     98     public void testLifecycleFromOnCreate() {
     99         mClientRecord.setState(ON_CREATE);
    100         assertArrayEquals(new int[] {}, path(ON_CREATE));
    101         assertArrayEquals(new int[] {ON_START}, path(ON_START));
    102         assertArrayEquals(new int[] {ON_START, ON_RESUME}, path(ON_RESUME));
    103         assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
    104         assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
    105         assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY},
    106                 path(ON_DESTROY));
    107     }
    108 
    109     @Test
    110     public void testLifecycleFromOnStart() {
    111         mClientRecord.setState(ON_START);
    112         assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE},
    113                 path(ON_CREATE));
    114         assertArrayEquals(new int[] {}, path(ON_START));
    115         assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
    116         assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
    117         assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
    118         assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
    119     }
    120 
    121     @Test
    122     public void testLifecycleFromOnResume() {
    123         mClientRecord.setState(ON_RESUME);
    124         assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE}, path(ON_CREATE));
    125         assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_RESTART, ON_START}, path(ON_START));
    126         assertArrayEquals(new int[] {}, path(ON_RESUME));
    127         assertArrayEquals(new int[] {ON_PAUSE}, path(ON_PAUSE));
    128         assertArrayEquals(new int[] {ON_PAUSE, ON_STOP}, path(ON_STOP));
    129         assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
    130     }
    131 
    132     @Test
    133     public void testLifecycleFromOnPause() {
    134         mClientRecord.setState(ON_PAUSE);
    135         assertArrayEquals(new int[] {ON_STOP, ON_DESTROY, ON_CREATE}, path(ON_CREATE));
    136         assertArrayEquals(new int[] {ON_STOP, ON_RESTART, ON_START}, path(ON_START));
    137         assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
    138         assertArrayEquals(new int[] {}, path(ON_PAUSE));
    139         assertArrayEquals(new int[] {ON_STOP}, path(ON_STOP));
    140         assertArrayEquals(new int[] {ON_STOP, ON_DESTROY}, path(ON_DESTROY));
    141     }
    142 
    143     @Test
    144     public void testLifecycleFromOnStop() {
    145         mClientRecord.setState(ON_STOP);
    146         assertArrayEquals(new int[] {ON_DESTROY, ON_CREATE}, path(ON_CREATE));
    147         assertArrayEquals(new int[] {ON_RESTART, ON_START}, path(ON_START));
    148         assertArrayEquals(new int[] {ON_RESTART, ON_START, ON_RESUME}, path(ON_RESUME));
    149         assertArrayEquals(new int[] {ON_RESTART, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
    150         assertArrayEquals(new int[] {}, path(ON_STOP));
    151         assertArrayEquals(new int[] {ON_DESTROY}, path(ON_DESTROY));
    152     }
    153 
    154     @Test
    155     public void testLifecycleFromOnDestroy() {
    156         mClientRecord.setState(ON_DESTROY);
    157         assertArrayEquals(new int[] {ON_CREATE}, path(ON_CREATE));
    158         assertArrayEquals(new int[] {ON_CREATE, ON_START}, path(ON_START));
    159         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, path(ON_RESUME));
    160         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
    161         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
    162                 path(ON_STOP));
    163         assertArrayEquals(new int[] {}, path(ON_DESTROY));
    164     }
    165 
    166     @Test
    167     public void testLifecycleExcludeLastItem() {
    168         mClientRecord.setState(PRE_ON_CREATE);
    169         assertArrayEquals(new int[] {}, pathExcludeLast(PRE_ON_CREATE));
    170         assertArrayEquals(new int[] {}, pathExcludeLast(ON_CREATE));
    171         assertArrayEquals(new int[] {ON_CREATE}, pathExcludeLast(ON_START));
    172         assertArrayEquals(new int[] {ON_CREATE, ON_START}, pathExcludeLast(ON_RESUME));
    173         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, pathExcludeLast(ON_PAUSE));
    174         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE},
    175                 pathExcludeLast(ON_STOP));
    176         assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
    177                 pathExcludeLast(ON_DESTROY));
    178     }
    179 
    180     @Test(expected = IllegalArgumentException.class)
    181     public void testLifecycleUndefinedStartState() {
    182         mClientRecord.setState(UNDEFINED);
    183         path(ON_CREATE);
    184     }
    185 
    186     @Test(expected = IllegalArgumentException.class)
    187     public void testLifecycleUndefinedFinishState() {
    188         mClientRecord.setState(PRE_ON_CREATE);
    189         path(UNDEFINED);
    190     }
    191 
    192     @Test(expected = IllegalArgumentException.class)
    193     public void testLifecycleInvalidPreOnCreateFinishState() {
    194         mClientRecord.setState(ON_CREATE);
    195         path(PRE_ON_CREATE);
    196     }
    197 
    198     @Test(expected = IllegalArgumentException.class)
    199     public void testLifecycleInvalidOnRestartStartState() {
    200         mClientRecord.setState(ON_RESTART);
    201         path(ON_RESUME);
    202     }
    203 
    204     @Test(expected = IllegalArgumentException.class)
    205     public void testLifecycleInvalidOnRestartFinishState() {
    206         mClientRecord.setState(ON_CREATE);
    207         path(ON_RESTART);
    208     }
    209 
    210     @Test
    211     public void testTransactionResolution() {
    212         ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
    213         when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
    214         ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
    215         when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
    216         ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
    217         IBinder token = mock(IBinder.class);
    218 
    219         ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
    220                 token /* activityToken */);
    221         transaction.addCallback(callback1);
    222         transaction.addCallback(callback2);
    223         transaction.setLifecycleStateRequest(stateRequest);
    224 
    225         transaction.preExecute(mTransactionHandler);
    226         mExecutor.execute(transaction);
    227 
    228         InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
    229         inOrder.verify(callback1, times(1)).execute(eq(mTransactionHandler), eq(token), any());
    230         inOrder.verify(callback2, times(1)).execute(eq(mTransactionHandler), eq(token), any());
    231         inOrder.verify(stateRequest, times(1)).execute(eq(mTransactionHandler), eq(token), any());
    232     }
    233 
    234     @Test
    235     public void testActivityResultRequiredStateResolution() {
    236         PostExecItem postExecItem = new PostExecItem(ON_RESUME);
    237 
    238         IBinder token = mock(IBinder.class);
    239         ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
    240                 token /* activityToken */);
    241         transaction.addCallback(postExecItem);
    242 
    243         // Verify resolution that should get to onPause
    244         mClientRecord.setState(ON_RESUME);
    245         mExecutor.executeCallbacks(transaction);
    246         verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE));
    247 
    248         // Verify resolution that should get to onStart
    249         mClientRecord.setState(ON_STOP);
    250         mExecutor.executeCallbacks(transaction);
    251         verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_START));
    252     }
    253 
    254     @Test
    255     public void testClosestStateResolutionForSameState() {
    256         final int[] allStates = new int[] {
    257                 ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY};
    258 
    259         mClientRecord.setState(ON_CREATE);
    260         assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord,
    261                 shuffledArray(allStates)));
    262 
    263         mClientRecord.setState(ON_START);
    264         assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord,
    265                 shuffledArray(allStates)));
    266 
    267         mClientRecord.setState(ON_RESUME);
    268         assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord,
    269                 shuffledArray(allStates)));
    270 
    271         mClientRecord.setState(ON_PAUSE);
    272         assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord,
    273                 shuffledArray(allStates)));
    274 
    275         mClientRecord.setState(ON_STOP);
    276         assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord,
    277                 shuffledArray(allStates)));
    278 
    279         mClientRecord.setState(ON_DESTROY);
    280         assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord,
    281                 shuffledArray(allStates)));
    282 
    283         mClientRecord.setState(PRE_ON_CREATE);
    284         assertEquals(PRE_ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord,
    285                 new int[] {PRE_ON_CREATE}));
    286     }
    287 
    288     @Test
    289     public void testClosestStateResolution() {
    290         mClientRecord.setState(PRE_ON_CREATE);
    291         assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    292                 new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
    293         assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    294                 new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
    295         assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    296                 new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
    297         assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    298                 new int[] {ON_PAUSE, ON_STOP, ON_DESTROY})));
    299         assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    300                 new int[] {ON_STOP, ON_DESTROY})));
    301         assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    302                 new int[] {ON_DESTROY})));
    303     }
    304 
    305     @Test
    306     public void testClosestStateResolutionFromOnCreate() {
    307         mClientRecord.setState(ON_CREATE);
    308         assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    309                 new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
    310     }
    311 
    312     @Test
    313     public void testClosestStateResolutionFromOnStart() {
    314         mClientRecord.setState(ON_START);
    315         assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    316                 new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
    317         assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    318                 new int[] {ON_CREATE})));
    319     }
    320 
    321     @Test
    322     public void testClosestStateResolutionFromOnResume() {
    323         mClientRecord.setState(ON_RESUME);
    324         assertEquals(ON_PAUSE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    325                 new int[] {ON_CREATE, ON_START, ON_PAUSE, ON_STOP, ON_DESTROY})));
    326         assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    327                 new int[] {ON_CREATE, ON_DESTROY})));
    328         assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    329                 new int[] {ON_CREATE, ON_START})));
    330         assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    331                 new int[] {ON_CREATE})));
    332     }
    333 
    334     @Test
    335     public void testClosestStateResolutionFromOnPause() {
    336         mClientRecord.setState(ON_PAUSE);
    337         assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    338                 new int[] {ON_CREATE, ON_START, ON_RESUME, ON_DESTROY})));
    339         assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    340                 new int[] {ON_CREATE, ON_START, ON_STOP, ON_DESTROY})));
    341         assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    342                 new int[] {ON_CREATE, ON_START})));
    343         assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    344                 new int[] {ON_CREATE})));
    345     }
    346 
    347     @Test
    348     public void testClosestStateResolutionFromOnStop() {
    349         mClientRecord.setState(ON_STOP);
    350         assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    351                 new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_DESTROY})));
    352         assertEquals(ON_DESTROY, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    353                 new int[] {ON_CREATE, ON_DESTROY})));
    354         assertEquals(ON_START, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    355                 new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE})));
    356     }
    357 
    358     @Test
    359     public void testClosestStateResolutionFromOnDestroy() {
    360         mClientRecord.setState(ON_DESTROY);
    361         assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
    362                 new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP})));
    363     }
    364 
    365     @Test
    366     public void testClosestStateResolutionToUndefined() {
    367         mClientRecord.setState(ON_CREATE);
    368         assertEquals(UNDEFINED,
    369                 mExecutorHelper.getClosestPreExecutionState(mClientRecord, UNDEFINED));
    370     }
    371 
    372     @Test
    373     public void testClosestStateResolutionToOnResume() {
    374         mClientRecord.setState(ON_DESTROY);
    375         assertEquals(ON_START,
    376                 mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
    377         mClientRecord.setState(ON_START);
    378         assertEquals(ON_START,
    379                 mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
    380         mClientRecord.setState(ON_PAUSE);
    381         assertEquals(ON_PAUSE,
    382                 mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
    383     }
    384 
    385     private static int[] shuffledArray(int[] inputArray) {
    386         final List<Integer> list = Arrays.stream(inputArray).boxed().collect(Collectors.toList());
    387         Collections.shuffle(list);
    388         return list.stream().mapToInt(Integer::intValue).toArray();
    389     }
    390 
    391     private int[] path(int finish) {
    392         return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish,
    393                 false /* excludeLastState */).toArray();
    394     }
    395 
    396     private int[] pathExcludeLast(int finish) {
    397         return mExecutorHelper.getLifecyclePath(mClientRecord.getLifecycleState(), finish,
    398                 true /* excludeLastState */).toArray();
    399     }
    400 
    401     /** A transaction item that requires some specific post-execution state. */
    402     private static class PostExecItem extends StubItem {
    403 
    404         @LifecycleState
    405         private int mPostExecutionState;
    406 
    407         PostExecItem(@LifecycleState int state) {
    408             mPostExecutionState = state;
    409         }
    410 
    411         @Override
    412         public int getPostExecutionState() {
    413             return mPostExecutionState;
    414         }
    415     }
    416 
    417     /** Stub implementation of a transaction item that works as a base class for items in tests. */
    418     private static class StubItem extends ClientTransactionItem  {
    419 
    420         private StubItem() {
    421         }
    422 
    423         private StubItem(Parcel in) {
    424         }
    425 
    426         @Override
    427         public void execute(ClientTransactionHandler client, IBinder token,
    428                 PendingTransactionActions pendingActions) {
    429         }
    430 
    431         @Override
    432         public void recycle() {
    433         }
    434 
    435         @Override
    436         public void writeToParcel(Parcel dest, int flags) {
    437         }
    438 
    439         public static final Parcelable.Creator<StubItem> CREATOR =
    440                 new Parcelable.Creator<StubItem>() {
    441                     public StubItem createFromParcel(Parcel in) {
    442                         return new StubItem(in);
    443                     }
    444 
    445                     public StubItem[] newArray(int size) {
    446                         return new StubItem[size];
    447                     }
    448                 };
    449     }
    450 }
    451