2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.util;
19 import android.os.Debug;
20 import android.os.HandlerThread;
21 import android.os.Looper;
22 import android.os.Message;
23 import android.os.SystemClock;
25 import com.android.internal.util.HierarchicalState;
26 import com.android.internal.util.HierarchicalStateMachine;
27 import com.android.internal.util.ProcessedMessages;
29 import android.test.suitebuilder.annotation.MediumTest;
30 import android.test.suitebuilder.annotation.SmallTest;
31 import android.util.Log;
33 import junit.framework.TestCase;
36 * Test for HierarchicalStateMachine.
38 public class HierarchicalStateMachineTest extends TestCase {
39 private static final int TEST_CMD_1 = 1;
40 private static final int TEST_CMD_2 = 2;
41 private static final int TEST_CMD_3 = 3;
42 private static final int TEST_CMD_4 = 4;
43 private static final int TEST_CMD_5 = 5;
44 private static final int TEST_CMD_6 = 6;
46 private static final boolean DBG = true;
47 private static final boolean WAIT_FOR_DEBUGGER = false;
48 private static final String TAG = "HierarchicalStateMachineTest";
51 * Tests that we can quit the state machine.
53 class StateMachineQuitTest extends HierarchicalStateMachine {
54 private int mQuitCount = 0;
56 StateMachineQuitTest(String name) {
61 // Setup state machine with 1 state
64 // Set the initial state
68 class S1 extends HierarchicalState {
69 @Override protected boolean processMessage(Message message) {
70 if (isQuit(message)) {
73 // Returning NOT_HANDLED to actually quit
80 // All other message are handled
87 protected void quitting() {
88 synchronized (mThisSm) {
93 private StateMachineQuitTest mThisSm;
94 private S1 mS1 = new S1();
98 public void testStateMachineQuitTest() throws Exception {
99 if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
101 StateMachineQuitTest smQuitTest = new StateMachineQuitTest("smQuitTest");
103 if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuitTest E");
105 synchronized (smQuitTest) {
107 for (int i = 1; i <= 6; i++) {
108 smQuitTest.sendMessage(i);
111 // First two are ignored
119 // wait for the messages to be handled
121 } catch (InterruptedException e) {
122 Log.e(TAG, "testStateMachineQuitTest: exception while waiting " + e.getMessage());
126 assertTrue(smQuitTest.getProcessedMessagesCount() == 9);
128 ProcessedMessages.Info pmi;
130 // The first two message didn't quit and were handled by mS1
131 pmi = smQuitTest.getProcessedMessage(6);
132 assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
133 assertEquals(smQuitTest.mS1, pmi.getState());
134 assertEquals(smQuitTest.mS1, pmi.getOriginalState());
136 pmi = smQuitTest.getProcessedMessage(7);
137 assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
138 assertEquals(smQuitTest.mS1, pmi.getState());
139 assertEquals(smQuitTest.mS1, pmi.getOriginalState());
141 // The last message was never handled so the states are null
142 pmi = smQuitTest.getProcessedMessage(8);
143 assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
144 assertEquals(null, pmi.getState());
145 assertEquals(null, pmi.getOriginalState());
147 if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuitTest X");
151 * Test enter/exit can use transitionTo
153 class StateMachineEnterExitTransitionToTest extends HierarchicalStateMachine {
154 StateMachineEnterExitTransitionToTest(String name) {
159 // Setup state machine with 1 state
165 // Set the initial state
166 setInitialState(mS1);
169 class S1 extends HierarchicalState {
170 @Override protected void enter() {
171 // Test that message is HSM_INIT_CMD
172 assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
174 // Test that a transition in enter and the initial state works
177 Log.d(TAG, "S1.enter");
179 @Override protected void exit() {
180 // Test that message is HSM_INIT_CMD
181 assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
184 Log.d(TAG, "S1.exit");
188 class S2 extends HierarchicalState {
189 @Override protected void enter() {
190 // Test that message is HSM_INIT_CMD
191 assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
194 Log.d(TAG, "S2.enter");
196 @Override protected void exit() {
197 // Test that message is TEST_CMD_1
198 assertEquals(TEST_CMD_1, getCurrentMessage().what);
200 // Test transition in exit work
203 Log.d(TAG, "S2.exit");
205 @Override protected boolean processMessage(Message message) {
206 // Start a transition to S3 but it will be
207 // changed to a transition to S4 in exit
209 Log.d(TAG, "S2.processMessage");
214 class S3 extends HierarchicalState {
215 @Override protected void enter() {
216 // Test that we can do halting in an enter/exit
217 transitionToHaltingState();
219 Log.d(TAG, "S3.enter");
221 @Override protected void exit() {
223 Log.d(TAG, "S3.exit");
228 class S4 extends HierarchicalState {
229 @Override protected void enter() {
230 // Test that we can do halting in an enter/exit
231 transitionToHaltingState();
233 Log.d(TAG, "S4.enter");
235 @Override protected void exit() {
237 Log.d(TAG, "S4.exit");
242 protected void halting() {
243 synchronized (mThisSm) {
248 private StateMachineEnterExitTransitionToTest mThisSm;
249 private S1 mS1 = new S1();
250 private S2 mS2 = new S2();
251 private S3 mS3 = new S3();
252 private S4 mS4 = new S4();
253 private int mS1EnterCount = 0;
254 private int mS1ExitCount = 0;
255 private int mS2EnterCount = 0;
256 private int mS2ExitCount = 0;
257 private int mS3EnterCount = 0;
258 private int mS3ExitCount = 0;
259 private int mS4EnterCount = 0;
260 private int mS4ExitCount = 0;
264 public void testStateMachineEnterExitTransitionToTest() throws Exception {
265 //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
267 StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest =
268 new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest");
269 smEnterExitTranstionToTest.start();
270 if (smEnterExitTranstionToTest.isDbg()) {
271 Log.d(TAG, "testStateMachineEnterExitTransitionToTest E");
274 synchronized (smEnterExitTranstionToTest) {
275 smEnterExitTranstionToTest.sendMessage(TEST_CMD_1);
278 // wait for the messages to be handled
279 smEnterExitTranstionToTest.wait();
280 } catch (InterruptedException e) {
281 Log.e(TAG, "testStateMachineEnterExitTransitionToTest: exception while waiting "
286 assertTrue(smEnterExitTranstionToTest.getProcessedMessagesCount() == 1);
288 ProcessedMessages.Info pmi;
290 // Message should be handled by mS2.
291 pmi = smEnterExitTranstionToTest.getProcessedMessage(0);
292 assertEquals(TEST_CMD_1, pmi.getWhat());
293 assertEquals(smEnterExitTranstionToTest.mS2, pmi.getState());
294 assertEquals(smEnterExitTranstionToTest.mS2, pmi.getOriginalState());
296 assertEquals(smEnterExitTranstionToTest.mS1EnterCount, 1);
297 assertEquals(smEnterExitTranstionToTest.mS1ExitCount, 1);
298 assertEquals(smEnterExitTranstionToTest.mS2EnterCount, 1);
299 assertEquals(smEnterExitTranstionToTest.mS2ExitCount, 1);
300 assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
301 assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
302 assertEquals(smEnterExitTranstionToTest.mS3EnterCount, 1);
303 assertEquals(smEnterExitTranstionToTest.mS3ExitCount, 1);
305 if (smEnterExitTranstionToTest.isDbg()) {
306 Log.d(TAG, "testStateMachineEnterExitTransitionToTest X");
311 * Tests that ProcessedMessage works as a circular buffer.
313 class StateMachine0 extends HierarchicalStateMachine {
314 StateMachine0(String name) {
318 setProcessedMessagesSize(3);
320 // Setup state machine with 1 state
323 // Set the initial state
324 setInitialState(mS1);
327 class S1 extends HierarchicalState {
328 @Override protected boolean processMessage(Message message) {
329 if (message.what == TEST_CMD_6) {
330 transitionToHaltingState();
337 protected void halting() {
338 synchronized (mThisSm) {
343 private StateMachine0 mThisSm;
344 private S1 mS1 = new S1();
348 public void testStateMachine0() throws Exception {
349 //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
351 StateMachine0 sm0 = new StateMachine0("sm0");
353 if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 E");
357 for (int i = 1; i <= 6; i++) {
358 sm0.sendMessage(sm0.obtainMessage(i));
362 // wait for the messages to be handled
364 } catch (InterruptedException e) {
365 Log.e(TAG, "testStateMachine0: exception while waiting " + e.getMessage());
369 assertTrue(sm0.getProcessedMessagesCount() == 6);
370 assertTrue(sm0.getProcessedMessagesSize() == 3);
372 ProcessedMessages.Info pmi;
373 pmi = sm0.getProcessedMessage(0);
374 assertEquals(TEST_CMD_4, pmi.getWhat());
375 assertEquals(sm0.mS1, pmi.getState());
376 assertEquals(sm0.mS1, pmi.getOriginalState());
378 pmi = sm0.getProcessedMessage(1);
379 assertEquals(TEST_CMD_5, pmi.getWhat());
380 assertEquals(sm0.mS1, pmi.getState());
381 assertEquals(sm0.mS1, pmi.getOriginalState());
383 pmi = sm0.getProcessedMessage(2);
384 assertEquals(TEST_CMD_6, pmi.getWhat());
385 assertEquals(sm0.mS1, pmi.getState());
386 assertEquals(sm0.mS1, pmi.getOriginalState());
388 if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 X");
392 * This tests enter/exit and transitions to the same state.
393 * The state machine has one state, it receives two messages
394 * in state mS1. With the first message it transitions to
395 * itself which causes it to be exited and reentered.
397 class StateMachine1 extends HierarchicalStateMachine {
398 StateMachine1(String name) {
403 // Setup state machine with 1 state
406 // Set the initial state
407 setInitialState(mS1);
408 if (DBG) Log.d(TAG, "StateMachine1: ctor X");
411 class S1 extends HierarchicalState {
412 @Override protected void enter() {
416 @Override protected boolean processMessage(Message message) {
417 if (message.what == TEST_CMD_1) {
418 assertEquals(1, mEnterCount);
419 assertEquals(0, mExitCount);
421 } else if (message.what == TEST_CMD_2) {
422 assertEquals(2, mEnterCount);
423 assertEquals(1, mExitCount);
424 transitionToHaltingState();
429 @Override protected void exit() {
435 protected void halting() {
436 synchronized (mThisSm) {
441 private StateMachine1 mThisSm;
442 private S1 mS1 = new S1();
444 private int mEnterCount;
445 private int mExitCount;
449 public void testStateMachine1() throws Exception {
450 StateMachine1 sm1 = new StateMachine1("sm1");
452 if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 E");
456 sm1.sendMessage(TEST_CMD_1);
457 sm1.sendMessage(TEST_CMD_2);
460 // wait for the messages to be handled
462 } catch (InterruptedException e) {
463 Log.e(TAG, "testStateMachine1: exception while waiting " + e.getMessage());
467 assertEquals(2, sm1.mEnterCount);
468 assertEquals(2, sm1.mExitCount);
470 assertTrue(sm1.getProcessedMessagesSize() == 2);
472 ProcessedMessages.Info pmi;
473 pmi = sm1.getProcessedMessage(0);
474 assertEquals(TEST_CMD_1, pmi.getWhat());
475 assertEquals(sm1.mS1, pmi.getState());
476 assertEquals(sm1.mS1, pmi.getOriginalState());
478 pmi = sm1.getProcessedMessage(1);
479 assertEquals(TEST_CMD_2, pmi.getWhat());
480 assertEquals(sm1.mS1, pmi.getState());
481 assertEquals(sm1.mS1, pmi.getOriginalState());
483 assertEquals(2, sm1.mEnterCount);
484 assertEquals(2, sm1.mExitCount);
486 if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 X");
490 * Test deferring messages and states with no parents. The state machine
491 * has two states, it receives two messages in state mS1 deferring them
492 * until what == TEST_CMD_2 and then transitions to state mS2. State
493 * mS2 then receives both of the deferred messages first TEST_CMD_1 and
496 class StateMachine2 extends HierarchicalStateMachine {
497 StateMachine2(String name) {
502 // Setup the hierarchy
506 // Set the initial state
507 setInitialState(mS1);
508 if (DBG) Log.d(TAG, "StateMachine2: ctor X");
511 class S1 extends HierarchicalState {
512 @Override protected void enter() {
516 @Override protected boolean processMessage(Message message) {
517 deferMessage(message);
518 if (message.what == TEST_CMD_2) {
524 @Override protected void exit() {
529 class S2 extends HierarchicalState {
530 @Override protected boolean processMessage(Message message) {
531 if (message.what == TEST_CMD_2) {
532 transitionToHaltingState();
539 protected void halting() {
540 synchronized (mThisSm) {
545 private StateMachine2 mThisSm;
546 private S1 mS1 = new S1();
547 private S2 mS2 = new S2();
549 private boolean mDidEnter = false;
550 private boolean mDidExit = false;
554 public void testStateMachine2() throws Exception {
555 StateMachine2 sm2 = new StateMachine2("sm2");
557 if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 E");
561 sm2.sendMessage(TEST_CMD_1);
562 sm2.sendMessage(TEST_CMD_2);
565 // wait for the messages to be handled
567 } catch (InterruptedException e) {
568 Log.e(TAG, "testStateMachine2: exception while waiting " + e.getMessage());
572 assertTrue(sm2.getProcessedMessagesSize() == 4);
574 ProcessedMessages.Info pmi;
575 pmi = sm2.getProcessedMessage(0);
576 assertEquals(TEST_CMD_1, pmi.getWhat());
577 assertEquals(sm2.mS1, pmi.getState());
579 pmi = sm2.getProcessedMessage(1);
580 assertEquals(TEST_CMD_2, pmi.getWhat());
581 assertEquals(sm2.mS1, pmi.getState());
583 pmi = sm2.getProcessedMessage(2);
584 assertEquals(TEST_CMD_1, pmi.getWhat());
585 assertEquals(sm2.mS2, pmi.getState());
587 pmi = sm2.getProcessedMessage(3);
588 assertEquals(TEST_CMD_2, pmi.getWhat());
589 assertEquals(sm2.mS2, pmi.getState());
591 assertTrue(sm2.mDidEnter);
592 assertTrue(sm2.mDidExit);
594 if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 X");
598 * Test that unhandled messages in a child are handled by the parent.
599 * When TEST_CMD_2 is received.
601 class StateMachine3 extends HierarchicalStateMachine {
602 StateMachine3(String name) {
607 // Setup the simplest hierarchy of two states
608 // mParentState and mChildState.
609 // (Use indentation to help visualize hierarchy)
610 addState(mParentState);
611 addState(mChildState, mParentState);
613 // Set the initial state will be the child
614 setInitialState(mChildState);
615 if (DBG) Log.d(TAG, "StateMachine3: ctor X");
618 class ParentState extends HierarchicalState {
619 @Override protected boolean processMessage(Message message) {
620 if (message.what == TEST_CMD_2) {
621 transitionToHaltingState();
627 class ChildState extends HierarchicalState {
628 @Override protected boolean processMessage(Message message) {
634 protected void halting() {
635 synchronized (mThisSm) {
640 private StateMachine3 mThisSm;
641 private ParentState mParentState = new ParentState();
642 private ChildState mChildState = new ChildState();
646 public void testStateMachine3() throws Exception {
647 StateMachine3 sm3 = new StateMachine3("sm3");
649 if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 E");
653 sm3.sendMessage(TEST_CMD_1);
654 sm3.sendMessage(TEST_CMD_2);
657 // wait for the messages to be handled
659 } catch (InterruptedException e) {
660 Log.e(TAG, "testStateMachine3: exception while waiting " + e.getMessage());
664 assertTrue(sm3.getProcessedMessagesSize() == 2);
666 ProcessedMessages.Info pmi;
667 pmi = sm3.getProcessedMessage(0);
668 assertEquals(TEST_CMD_1, pmi.getWhat());
669 assertEquals(sm3.mParentState, pmi.getState());
670 assertEquals(sm3.mChildState, pmi.getOriginalState());
672 pmi = sm3.getProcessedMessage(1);
673 assertEquals(TEST_CMD_2, pmi.getWhat());
674 assertEquals(sm3.mParentState, pmi.getState());
675 assertEquals(sm3.mChildState, pmi.getOriginalState());
677 if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 X");
681 * Test a hierarchy of 3 states a parent and two children
682 * with transition from child 1 to child 2 and child 2
683 * lets the parent handle the messages.
685 class StateMachine4 extends HierarchicalStateMachine {
686 StateMachine4(String name) {
691 // Setup a hierarchy of three states
692 // mParentState, mChildState1 & mChildState2
693 // (Use indentation to help visualize hierarchy)
694 addState(mParentState);
695 addState(mChildState1, mParentState);
696 addState(mChildState2, mParentState);
698 // Set the initial state will be child 1
699 setInitialState(mChildState1);
700 if (DBG) Log.d(TAG, "StateMachine4: ctor X");
703 class ParentState extends HierarchicalState {
704 @Override protected boolean processMessage(Message message) {
705 if (message.what == TEST_CMD_2) {
706 transitionToHaltingState();
712 class ChildState1 extends HierarchicalState {
713 @Override protected boolean processMessage(Message message) {
714 transitionTo(mChildState2);
719 class ChildState2 extends HierarchicalState {
720 @Override protected boolean processMessage(Message message) {
726 protected void halting() {
727 synchronized (mThisSm) {
732 private StateMachine4 mThisSm;
733 private ParentState mParentState = new ParentState();
734 private ChildState1 mChildState1 = new ChildState1();
735 private ChildState2 mChildState2 = new ChildState2();
739 public void testStateMachine4() throws Exception {
740 StateMachine4 sm4 = new StateMachine4("sm4");
742 if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 E");
746 sm4.sendMessage(TEST_CMD_1);
747 sm4.sendMessage(TEST_CMD_2);
750 // wait for the messages to be handled
752 } catch (InterruptedException e) {
753 Log.e(TAG, "testStateMachine4: exception while waiting " + e.getMessage());
758 assertTrue(sm4.getProcessedMessagesSize() == 2);
760 ProcessedMessages.Info pmi;
761 pmi = sm4.getProcessedMessage(0);
762 assertEquals(TEST_CMD_1, pmi.getWhat());
763 assertEquals(sm4.mChildState1, pmi.getState());
764 assertEquals(sm4.mChildState1, pmi.getOriginalState());
766 pmi = sm4.getProcessedMessage(1);
767 assertEquals(TEST_CMD_2, pmi.getWhat());
768 assertEquals(sm4.mParentState, pmi.getState());
769 assertEquals(sm4.mChildState2, pmi.getOriginalState());
771 if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 X");
775 * Test transition from one child to another of a "complex"
776 * hierarchy with two parents and multiple children.
778 class StateMachine5 extends HierarchicalStateMachine {
779 StateMachine5(String name) {
784 // Setup a hierarchy with two parents and some children.
785 // (Use indentation to help visualize hierarchy)
786 addState(mParentState1);
787 addState(mChildState1, mParentState1);
788 addState(mChildState2, mParentState1);
790 addState(mParentState2);
791 addState(mChildState3, mParentState2);
792 addState(mChildState4, mParentState2);
793 addState(mChildState5, mChildState4);
795 // Set the initial state will be the child
796 setInitialState(mChildState1);
797 if (DBG) Log.d(TAG, "StateMachine5: ctor X");
800 class ParentState1 extends HierarchicalState {
801 @Override protected void enter() {
802 mParentState1EnterCount += 1;
804 @Override protected boolean processMessage(Message message) {
807 @Override protected void exit() {
808 mParentState1ExitCount += 1;
812 class ChildState1 extends HierarchicalState {
813 @Override protected void enter() {
814 mChildState1EnterCount += 1;
816 @Override protected boolean processMessage(Message message) {
817 assertEquals(1, mParentState1EnterCount);
818 assertEquals(0, mParentState1ExitCount);
819 assertEquals(1, mChildState1EnterCount);
820 assertEquals(0, mChildState1ExitCount);
821 assertEquals(0, mChildState2EnterCount);
822 assertEquals(0, mChildState2ExitCount);
823 assertEquals(0, mParentState2EnterCount);
824 assertEquals(0, mParentState2ExitCount);
825 assertEquals(0, mChildState3EnterCount);
826 assertEquals(0, mChildState3ExitCount);
827 assertEquals(0, mChildState4EnterCount);
828 assertEquals(0, mChildState4ExitCount);
829 assertEquals(0, mChildState5EnterCount);
830 assertEquals(0, mChildState5ExitCount);
832 transitionTo(mChildState2);
835 @Override protected void exit() {
836 mChildState1ExitCount += 1;
840 class ChildState2 extends HierarchicalState {
841 @Override protected void enter() {
842 mChildState2EnterCount += 1;
844 @Override protected boolean processMessage(Message message) {
845 assertEquals(1, mParentState1EnterCount);
846 assertEquals(0, mParentState1ExitCount);
847 assertEquals(1, mChildState1EnterCount);
848 assertEquals(1, mChildState1ExitCount);
849 assertEquals(1, mChildState2EnterCount);
850 assertEquals(0, mChildState2ExitCount);
851 assertEquals(0, mParentState2EnterCount);
852 assertEquals(0, mParentState2ExitCount);
853 assertEquals(0, mChildState3EnterCount);
854 assertEquals(0, mChildState3ExitCount);
855 assertEquals(0, mChildState4EnterCount);
856 assertEquals(0, mChildState4ExitCount);
857 assertEquals(0, mChildState5EnterCount);
858 assertEquals(0, mChildState5ExitCount);
860 transitionTo(mChildState5);
863 @Override protected void exit() {
864 mChildState2ExitCount += 1;
868 class ParentState2 extends HierarchicalState {
869 @Override protected void enter() {
870 mParentState2EnterCount += 1;
872 @Override protected boolean processMessage(Message message) {
873 assertEquals(1, mParentState1EnterCount);
874 assertEquals(1, mParentState1ExitCount);
875 assertEquals(1, mChildState1EnterCount);
876 assertEquals(1, mChildState1ExitCount);
877 assertEquals(1, mChildState2EnterCount);
878 assertEquals(1, mChildState2ExitCount);
879 assertEquals(2, mParentState2EnterCount);
880 assertEquals(1, mParentState2ExitCount);
881 assertEquals(1, mChildState3EnterCount);
882 assertEquals(1, mChildState3ExitCount);
883 assertEquals(2, mChildState4EnterCount);
884 assertEquals(2, mChildState4ExitCount);
885 assertEquals(1, mChildState5EnterCount);
886 assertEquals(1, mChildState5ExitCount);
888 transitionToHaltingState();
891 @Override protected void exit() {
892 mParentState2ExitCount += 1;
896 class ChildState3 extends HierarchicalState {
897 @Override protected void enter() {
898 mChildState3EnterCount += 1;
900 @Override protected boolean processMessage(Message message) {
901 assertEquals(1, mParentState1EnterCount);
902 assertEquals(1, mParentState1ExitCount);
903 assertEquals(1, mChildState1EnterCount);
904 assertEquals(1, mChildState1ExitCount);
905 assertEquals(1, mChildState2EnterCount);
906 assertEquals(1, mChildState2ExitCount);
907 assertEquals(1, mParentState2EnterCount);
908 assertEquals(0, mParentState2ExitCount);
909 assertEquals(1, mChildState3EnterCount);
910 assertEquals(0, mChildState3ExitCount);
911 assertEquals(1, mChildState4EnterCount);
912 assertEquals(1, mChildState4ExitCount);
913 assertEquals(1, mChildState5EnterCount);
914 assertEquals(1, mChildState5ExitCount);
916 transitionTo(mChildState4);
919 @Override protected void exit() {
920 mChildState3ExitCount += 1;
924 class ChildState4 extends HierarchicalState {
925 @Override protected void enter() {
926 mChildState4EnterCount += 1;
928 @Override protected boolean processMessage(Message message) {
929 assertEquals(1, mParentState1EnterCount);
930 assertEquals(1, mParentState1ExitCount);
931 assertEquals(1, mChildState1EnterCount);
932 assertEquals(1, mChildState1ExitCount);
933 assertEquals(1, mChildState2EnterCount);
934 assertEquals(1, mChildState2ExitCount);
935 assertEquals(1, mParentState2EnterCount);
936 assertEquals(0, mParentState2ExitCount);
937 assertEquals(1, mChildState3EnterCount);
938 assertEquals(1, mChildState3ExitCount);
939 assertEquals(2, mChildState4EnterCount);
940 assertEquals(1, mChildState4ExitCount);
941 assertEquals(1, mChildState5EnterCount);
942 assertEquals(1, mChildState5ExitCount);
944 transitionTo(mParentState2);
947 @Override protected void exit() {
948 mChildState4ExitCount += 1;
952 class ChildState5 extends HierarchicalState {
953 @Override protected void enter() {
954 mChildState5EnterCount += 1;
956 @Override protected boolean processMessage(Message message) {
957 assertEquals(1, mParentState1EnterCount);
958 assertEquals(1, mParentState1ExitCount);
959 assertEquals(1, mChildState1EnterCount);
960 assertEquals(1, mChildState1ExitCount);
961 assertEquals(1, mChildState2EnterCount);
962 assertEquals(1, mChildState2ExitCount);
963 assertEquals(1, mParentState2EnterCount);
964 assertEquals(0, mParentState2ExitCount);
965 assertEquals(0, mChildState3EnterCount);
966 assertEquals(0, mChildState3ExitCount);
967 assertEquals(1, mChildState4EnterCount);
968 assertEquals(0, mChildState4ExitCount);
969 assertEquals(1, mChildState5EnterCount);
970 assertEquals(0, mChildState5ExitCount);
972 transitionTo(mChildState3);
975 @Override protected void exit() {
976 mChildState5ExitCount += 1;
981 protected void halting() {
982 synchronized (mThisSm) {
987 private StateMachine5 mThisSm;
988 private ParentState1 mParentState1 = new ParentState1();
989 private ChildState1 mChildState1 = new ChildState1();
990 private ChildState2 mChildState2 = new ChildState2();
991 private ParentState2 mParentState2 = new ParentState2();
992 private ChildState3 mChildState3 = new ChildState3();
993 private ChildState4 mChildState4 = new ChildState4();
994 private ChildState5 mChildState5 = new ChildState5();
996 private int mParentState1EnterCount = 0;
997 private int mParentState1ExitCount = 0;
998 private int mChildState1EnterCount = 0;
999 private int mChildState1ExitCount = 0;
1000 private int mChildState2EnterCount = 0;
1001 private int mChildState2ExitCount = 0;
1002 private int mParentState2EnterCount = 0;
1003 private int mParentState2ExitCount = 0;
1004 private int mChildState3EnterCount = 0;
1005 private int mChildState3ExitCount = 0;
1006 private int mChildState4EnterCount = 0;
1007 private int mChildState4ExitCount = 0;
1008 private int mChildState5EnterCount = 0;
1009 private int mChildState5ExitCount = 0;
1013 public void testStateMachine5() throws Exception {
1014 StateMachine5 sm5 = new StateMachine5("sm5");
1016 if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 E");
1018 synchronized (sm5) {
1020 sm5.sendMessage(TEST_CMD_1);
1021 sm5.sendMessage(TEST_CMD_2);
1022 sm5.sendMessage(TEST_CMD_3);
1023 sm5.sendMessage(TEST_CMD_4);
1024 sm5.sendMessage(TEST_CMD_5);
1025 sm5.sendMessage(TEST_CMD_6);
1028 // wait for the messages to be handled
1030 } catch (InterruptedException e) {
1031 Log.e(TAG, "testStateMachine5: exception while waiting " + e.getMessage());
1036 assertTrue(sm5.getProcessedMessagesSize() == 6);
1038 assertEquals(1, sm5.mParentState1EnterCount);
1039 assertEquals(1, sm5.mParentState1ExitCount);
1040 assertEquals(1, sm5.mChildState1EnterCount);
1041 assertEquals(1, sm5.mChildState1ExitCount);
1042 assertEquals(1, sm5.mChildState2EnterCount);
1043 assertEquals(1, sm5.mChildState2ExitCount);
1044 assertEquals(2, sm5.mParentState2EnterCount);
1045 assertEquals(2, sm5.mParentState2ExitCount);
1046 assertEquals(1, sm5.mChildState3EnterCount);
1047 assertEquals(1, sm5.mChildState3ExitCount);
1048 assertEquals(2, sm5.mChildState4EnterCount);
1049 assertEquals(2, sm5.mChildState4ExitCount);
1050 assertEquals(1, sm5.mChildState5EnterCount);
1051 assertEquals(1, sm5.mChildState5ExitCount);
1053 ProcessedMessages.Info pmi;
1054 pmi = sm5.getProcessedMessage(0);
1055 assertEquals(TEST_CMD_1, pmi.getWhat());
1056 assertEquals(sm5.mChildState1, pmi.getState());
1057 assertEquals(sm5.mChildState1, pmi.getOriginalState());
1059 pmi = sm5.getProcessedMessage(1);
1060 assertEquals(TEST_CMD_2, pmi.getWhat());
1061 assertEquals(sm5.mChildState2, pmi.getState());
1062 assertEquals(sm5.mChildState2, pmi.getOriginalState());
1064 pmi = sm5.getProcessedMessage(2);
1065 assertEquals(TEST_CMD_3, pmi.getWhat());
1066 assertEquals(sm5.mChildState5, pmi.getState());
1067 assertEquals(sm5.mChildState5, pmi.getOriginalState());
1069 pmi = sm5.getProcessedMessage(3);
1070 assertEquals(TEST_CMD_4, pmi.getWhat());
1071 assertEquals(sm5.mChildState3, pmi.getState());
1072 assertEquals(sm5.mChildState3, pmi.getOriginalState());
1074 pmi = sm5.getProcessedMessage(4);
1075 assertEquals(TEST_CMD_5, pmi.getWhat());
1076 assertEquals(sm5.mChildState4, pmi.getState());
1077 assertEquals(sm5.mChildState4, pmi.getOriginalState());
1079 pmi = sm5.getProcessedMessage(5);
1080 assertEquals(TEST_CMD_6, pmi.getWhat());
1081 assertEquals(sm5.mParentState2, pmi.getState());
1082 assertEquals(sm5.mParentState2, pmi.getOriginalState());
1084 if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 X");
1088 * Test that the initial state enter is invoked immediately
1089 * after construction and before any other messages arrive and that
1090 * sendMessageDelayed works.
1092 class StateMachine6 extends HierarchicalStateMachine {
1093 StateMachine6(String name) {
1098 // Setup state machine with 1 state
1101 // Set the initial state
1102 setInitialState(mS1);
1103 if (DBG) Log.d(TAG, "StateMachine6: ctor X");
1106 class S1 extends HierarchicalState {
1108 @Override protected void enter() {
1109 sendMessage(TEST_CMD_1);
1112 @Override protected boolean processMessage(Message message) {
1113 if (message.what == TEST_CMD_1) {
1114 mArrivalTimeMsg1 = SystemClock.elapsedRealtime();
1115 } else if (message.what == TEST_CMD_2) {
1116 mArrivalTimeMsg2 = SystemClock.elapsedRealtime();
1117 transitionToHaltingState();
1122 @Override protected void exit() {
1127 protected void halting() {
1128 synchronized (mThisSm) {
1129 mThisSm.notifyAll();
1133 private StateMachine6 mThisSm;
1134 private S1 mS1 = new S1();
1136 private long mArrivalTimeMsg1;
1137 private long mArrivalTimeMsg2;
1141 public void testStateMachine6() throws Exception {
1143 final int DELAY_TIME = 250;
1144 final int DELAY_FUDGE = 20;
1146 StateMachine6 sm6 = new StateMachine6("sm6");
1148 if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 E");
1150 synchronized (sm6) {
1152 sentTimeMsg2 = SystemClock.elapsedRealtime();
1153 sm6.sendMessageDelayed(TEST_CMD_2, DELAY_TIME);
1156 // wait for the messages to be handled
1158 } catch (InterruptedException e) {
1159 Log.e(TAG, "testStateMachine6: exception while waiting " + e.getMessage());
1164 * TEST_CMD_1 was sent in enter and must always have been processed
1165 * immediately after construction and hence the arrival time difference
1166 * should always >= to the DELAY_TIME
1168 long arrivalTimeDiff = sm6.mArrivalTimeMsg2 - sm6.mArrivalTimeMsg1;
1169 long expectedDelay = DELAY_TIME - DELAY_FUDGE;
1170 if (sm6.isDbg()) Log.d(TAG, "testStateMachine6: expect " + arrivalTimeDiff
1171 + " >= " + expectedDelay);
1172 assertTrue(arrivalTimeDiff >= expectedDelay);
1174 if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 X");
1178 * Test that enter is invoked immediately after exit. This validates
1179 * that enter can be used to send a watch dog message for its state.
1181 class StateMachine7 extends HierarchicalStateMachine {
1182 private final int SM7_DELAY_TIME = 250;
1184 StateMachine7(String name) {
1189 // Setup state machine with 1 state
1193 // Set the initial state
1194 setInitialState(mS1);
1195 if (DBG) Log.d(TAG, "StateMachine7: ctor X");
1198 class S1 extends HierarchicalState {
1199 @Override protected boolean processMessage(Message message) {
1203 @Override protected void exit() {
1204 sendMessage(TEST_CMD_2);
1208 class S2 extends HierarchicalState {
1210 @Override protected void enter() {
1211 // Send a delayed message as a watch dog
1212 sendMessageDelayed(TEST_CMD_3, SM7_DELAY_TIME);
1215 @Override protected boolean processMessage(Message message) {
1216 if (message.what == TEST_CMD_2) {
1218 mArrivalTimeMsg2 = SystemClock.elapsedRealtime();
1219 } else if (message.what == TEST_CMD_3) {
1221 mArrivalTimeMsg3 = SystemClock.elapsedRealtime();
1224 if (mMsgCount == 2) {
1225 transitionToHaltingState();
1230 @Override protected void exit() {
1235 protected void halting() {
1236 synchronized (mThisSm) {
1237 mThisSm.notifyAll();
1241 private StateMachine7 mThisSm;
1242 private S1 mS1 = new S1();
1243 private S2 mS2 = new S2();
1245 private int mMsgCount = 0;
1246 private long mArrivalTimeMsg2;
1247 private long mArrivalTimeMsg3;
1251 public void testStateMachine7() throws Exception {
1253 final int SM7_DELAY_FUDGE = 20;
1255 StateMachine7 sm7 = new StateMachine7("sm7");
1257 if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 E");
1259 synchronized (sm7) {
1261 sentTimeMsg2 = SystemClock.elapsedRealtime();
1262 sm7.sendMessage(TEST_CMD_1);
1265 // wait for the messages to be handled
1267 } catch (InterruptedException e) {
1268 Log.e(TAG, "testStateMachine7: exception while waiting " + e.getMessage());
1273 * TEST_CMD_3 was sent in S2.enter with a delay and must always have been
1274 * processed immediately after S1.exit. Since S1.exit sent TEST_CMD_2
1275 * without a delay the arrival time difference should always >= to SM7_DELAY_TIME.
1277 long arrivalTimeDiff = sm7.mArrivalTimeMsg3 - sm7.mArrivalTimeMsg2;
1278 long expectedDelay = sm7.SM7_DELAY_TIME - SM7_DELAY_FUDGE;
1279 if (sm7.isDbg()) Log.d(TAG, "testStateMachine7: expect " + arrivalTimeDiff
1280 + " >= " + expectedDelay);
1281 assertTrue(arrivalTimeDiff >= expectedDelay);
1283 if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 X");
1287 * Test unhandledMessage.
1289 class StateMachineUnhandledMessage extends HierarchicalStateMachine {
1290 StateMachineUnhandledMessage(String name) {
1295 // Setup state machine with 1 state
1298 // Set the initial state
1299 setInitialState(mS1);
1302 @Override protected void unhandledMessage(Message message) {
1303 mUnhandledMessageCount += 1;
1306 class S1 extends HierarchicalState {
1307 @Override protected boolean processMessage(Message message) {
1308 if (message.what == TEST_CMD_2) {
1309 transitionToHaltingState();
1316 protected void halting() {
1317 synchronized (mThisSm) {
1318 mThisSm.notifyAll();
1322 private StateMachineUnhandledMessage mThisSm;
1323 private int mUnhandledMessageCount;
1324 private S1 mS1 = new S1();
1328 public void testStateMachineUnhandledMessage() throws Exception {
1330 StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("sm");
1332 if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage E");
1336 for (int i = 1; i <= 2; i++) {
1341 // wait for the messages to be handled
1343 } catch (InterruptedException e) {
1344 Log.e(TAG, "testStateMachineUnhandledMessage: exception while waiting "
1349 assertTrue(sm.getProcessedMessagesCount() == 2);
1350 assertEquals(2, sm.mUnhandledMessageCount);
1352 if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage X");
1356 * Test state machines sharing the same thread/looper. Multiple instances
1357 * of the same state machine will be created. They will all share the
1358 * same thread and thus each can update <code>sharedCounter</code> which
1359 * will be used to notify testStateMachineSharedThread that the test is
1362 class StateMachineSharedThread extends HierarchicalStateMachine {
1363 StateMachineSharedThread(String name, Looper looper, int maxCount) {
1364 super(name, looper);
1365 mMaxCount = maxCount;
1368 // Setup state machine with 1 state
1371 // Set the initial state
1372 setInitialState(mS1);
1375 class S1 extends HierarchicalState {
1376 @Override protected boolean processMessage(Message message) {
1377 if (message.what == TEST_CMD_4) {
1378 transitionToHaltingState();
1385 protected void halting() {
1386 // Update the shared counter, which is OK since all state
1387 // machines are using the same thread.
1389 if (sharedCounter == mMaxCount) {
1390 synchronized (waitObject) {
1391 waitObject.notifyAll();
1396 private int mMaxCount;
1397 private S1 mS1 = new S1();
1399 private static int sharedCounter = 0;
1400 private static Object waitObject = new Object();
1403 public void testStateMachineSharedThread() throws Exception {
1404 if (DBG) Log.d(TAG, "testStateMachineSharedThread E");
1406 // Create and start the handler thread
1407 HandlerThread smThread = new HandlerThread("testStateMachineSharedThread");
1410 // Create the state machines
1411 StateMachineSharedThread sms[] = new StateMachineSharedThread[10];
1412 for (int i = 0; i < sms.length; i++) {
1413 sms[i] = new StateMachineSharedThread("sm", smThread.getLooper(), sms.length);
1417 synchronized (waitObject) {
1418 // Send messages to each of the state machines
1419 for (StateMachineSharedThread sm : sms) {
1420 for (int i = 1; i <= 4; i++) {
1425 // Wait for the last state machine to notify its done
1428 } catch (InterruptedException e) {
1429 Log.e(TAG, "testStateMachineSharedThread: exception while waiting "
1434 for (StateMachineSharedThread sm : sms) {
1435 assertTrue(sm.getProcessedMessagesCount() == 4);
1436 for (int i = 0; i < sm.getProcessedMessagesCount(); i++) {
1437 ProcessedMessages.Info pmi = sm.getProcessedMessage(i);
1438 assertEquals(i+1, pmi.getWhat());
1439 assertEquals(sm.mS1, pmi.getState());
1440 assertEquals(sm.mS1, pmi.getOriginalState());
1444 if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
1448 public void testHsm1() throws Exception {
1449 if (DBG) Log.d(TAG, "testHsm1 E");
1451 Hsm1 sm = Hsm1.makeHsm1();
1454 sm.sendMessage(Hsm1.CMD_1);
1455 sm.sendMessage(Hsm1.CMD_2);
1458 // Wait for the last state machine to notify its done
1461 } catch (InterruptedException e) {
1462 Log.e(TAG, "testHsm1: exception while waiting " + e.getMessage());
1466 assertEquals(7, sm.getProcessedMessagesCount());
1467 ProcessedMessages.Info pmi = sm.getProcessedMessage(0);
1468 assertEquals(Hsm1.CMD_1, pmi.getWhat());
1469 assertEquals(sm.mS1, pmi.getState());
1470 assertEquals(sm.mS1, pmi.getOriginalState());
1472 pmi = sm.getProcessedMessage(1);
1473 assertEquals(Hsm1.CMD_2, pmi.getWhat());
1474 assertEquals(sm.mP1, pmi.getState());
1475 assertEquals(sm.mS1, pmi.getOriginalState());
1477 pmi = sm.getProcessedMessage(2);
1478 assertEquals(Hsm1.CMD_2, pmi.getWhat());
1479 assertEquals(sm.mS2, pmi.getState());
1480 assertEquals(sm.mS2, pmi.getOriginalState());
1482 pmi = sm.getProcessedMessage(3);
1483 assertEquals(Hsm1.CMD_3, pmi.getWhat());
1484 assertEquals(sm.mS2, pmi.getState());
1485 assertEquals(sm.mS2, pmi.getOriginalState());
1487 pmi = sm.getProcessedMessage(4);
1488 assertEquals(Hsm1.CMD_3, pmi.getWhat());
1489 assertEquals(sm.mP2, pmi.getState());
1490 assertEquals(sm.mP2, pmi.getOriginalState());
1492 pmi = sm.getProcessedMessage(5);
1493 assertEquals(Hsm1.CMD_4, pmi.getWhat());
1494 assertEquals(sm.mP2, pmi.getState());
1495 assertEquals(sm.mP2, pmi.getOriginalState());
1497 pmi = sm.getProcessedMessage(6);
1498 assertEquals(Hsm1.CMD_5, pmi.getWhat());
1499 assertEquals(sm.mP2, pmi.getState());
1500 assertEquals(sm.mP2, pmi.getOriginalState());
1502 if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
1506 class Hsm1 extends HierarchicalStateMachine {
1507 private static final String TAG = "hsm1";
1509 public static final int CMD_1 = 1;
1510 public static final int CMD_2 = 2;
1511 public static final int CMD_3 = 3;
1512 public static final int CMD_4 = 4;
1513 public static final int CMD_5 = 5;
1515 public static Hsm1 makeHsm1() {
1516 Log.d(TAG, "makeHsm1 E");
1517 Hsm1 sm = new Hsm1("hsm1");
1519 Log.d(TAG, "makeHsm1 X");
1525 Log.d(TAG, "ctor E");
1527 // Add states, use indentation to show hierarchy
1533 // Set the initial state
1534 setInitialState(mS1);
1535 Log.d(TAG, "ctor X");
1538 class P1 extends HierarchicalState {
1539 @Override protected void enter() {
1540 Log.d(TAG, "P1.enter");
1542 @Override protected boolean processMessage(Message message) {
1544 Log.d(TAG, "P1.processMessage what=" + message.what);
1545 switch(message.what) {
1547 // CMD_2 will arrive in mS2 before CMD_3
1549 deferMessage(message);
1554 // Any message we don't understand in this state invokes unhandledMessage
1560 @Override protected void exit() {
1561 Log.d(TAG, "P1.exit");
1565 class S1 extends HierarchicalState {
1566 @Override protected void enter() {
1567 Log.d(TAG, "S1.enter");
1569 @Override protected boolean processMessage(Message message) {
1570 Log.d(TAG, "S1.processMessage what=" + message.what);
1571 if (message.what == CMD_1) {
1572 // Transition to ourself to show that enter/exit is called
1576 // Let parent process all other messages
1580 @Override protected void exit() {
1581 Log.d(TAG, "S1.exit");
1585 class S2 extends HierarchicalState {
1586 @Override protected void enter() {
1587 Log.d(TAG, "S2.enter");
1589 @Override protected boolean processMessage(Message message) {
1591 Log.d(TAG, "S2.processMessage what=" + message.what);
1592 switch(message.what) {
1598 deferMessage(message);
1608 @Override protected void exit() {
1609 Log.d(TAG, "S2.exit");
1613 class P2 extends HierarchicalState {
1614 @Override protected void enter() {
1615 Log.d(TAG, "P2.enter");
1618 @Override protected boolean processMessage(Message message) {
1619 Log.d(TAG, "P2.processMessage what=" + message.what);
1620 switch(message.what) {
1626 transitionToHaltingState();
1631 @Override protected void exit() {
1632 Log.d(TAG, "P2.exit");
1637 protected void halting() {
1638 Log.d(TAG, "halting");
1639 synchronized (this) {