1 /** 2 * Copyright (C) 2009 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 com.android.internal.util; 18 19 import java.util.Collection; 20 import java.util.Iterator; 21 22 import android.os.Debug; 23 import android.os.HandlerThread; 24 import android.os.Looper; 25 import android.os.Message; 26 import android.os.SystemClock; 27 28 import android.test.suitebuilder.annotation.Suppress; 29 import com.android.internal.util.State; 30 import com.android.internal.util.StateMachine; 31 import com.android.internal.util.StateMachine.LogRec; 32 33 import android.test.suitebuilder.annotation.MediumTest; 34 import android.test.suitebuilder.annotation.SmallTest; 35 import android.util.Log; 36 37 import junit.framework.TestCase; 38 39 /** 40 * Test for StateMachine. 41 */ 42 public class StateMachineTest extends TestCase { 43 private static final String ENTER = "enter"; 44 private static final String EXIT = "exit"; 45 private static final String ON_QUITTING = "ON_QUITTING"; 46 47 private static final int TEST_CMD_1 = 1; 48 private static final int TEST_CMD_2 = 2; 49 private static final int TEST_CMD_3 = 3; 50 private static final int TEST_CMD_4 = 4; 51 private static final int TEST_CMD_5 = 5; 52 private static final int TEST_CMD_6 = 6; 53 54 private static final boolean DBG = true; 55 private static final boolean WAIT_FOR_DEBUGGER = false; 56 private static final String TAG = "StateMachineTest"; 57 58 private void sleep(int millis) { 59 try { 60 Thread.sleep(millis); 61 } catch(InterruptedException e) { 62 } 63 } 64 65 private void dumpLogRecs(StateMachine sm) { 66 int size = sm.getLogRecSize(); 67 tlog("size=" + size + " count=" + sm.getLogRecCount()); 68 for (int i = 0; i < size; i++) { 69 LogRec lr = sm.getLogRec(i); 70 tlog(lr.toString()); 71 } 72 } 73 74 private void dumpLogRecs(Collection<LogRec> clr) { 75 int size = clr.size(); 76 tlog("size=" + size); 77 for (LogRec lr : clr) { 78 tlog(lr.toString()); 79 } 80 } 81 82 /** 83 * Tests {@link StateMachine#toString()}. 84 */ 85 class StateMachineToStringTest extends StateMachine { 86 StateMachineToStringTest(String name) { 87 super(name); 88 } 89 } 90 91 class ExampleState extends State { 92 String mName; 93 94 ExampleState(String name) { 95 mName = name; 96 } 97 98 @Override 99 public String getName() { 100 return mName; 101 } 102 } 103 104 @SmallTest 105 public void testToStringSucceedsEvenIfMachineHasNoStates() throws Exception { 106 StateMachine stateMachine = new StateMachineToStringTest("TestStateMachine"); 107 assertTrue(stateMachine.toString().contains("TestStateMachine")); 108 } 109 110 @SmallTest 111 public void testToStringSucceedsEvenIfStateHasNoName() throws Exception { 112 StateMachine stateMachine = new StateMachineToStringTest("TestStateMachine"); 113 State exampleState = new ExampleState(null); 114 stateMachine.addState(exampleState); 115 stateMachine.setInitialState(exampleState); 116 stateMachine.start(); 117 assertTrue(stateMachine.toString().contains("TestStateMachine")); 118 assertTrue(stateMachine.toString().contains("(null)")); 119 } 120 121 @SmallTest 122 public void testToStringIncludesMachineAndStateNames() throws Exception { 123 StateMachine stateMachine = new StateMachineToStringTest("TestStateMachine"); 124 State exampleState = new ExampleState("exampleState"); 125 stateMachine.addState(exampleState); 126 stateMachine.setInitialState(exampleState); 127 stateMachine.start(); 128 assertTrue(stateMachine.toString().contains("TestStateMachine")); 129 assertTrue(stateMachine.toString().contains("exampleState")); 130 } 131 132 @SmallTest 133 public void testToStringDoesNotContainMultipleLines() throws Exception { 134 StateMachine stateMachine = new StateMachineToStringTest("TestStateMachine"); 135 State exampleState = new ExampleState("exampleState"); 136 stateMachine.addState(exampleState); 137 stateMachine.setInitialState(exampleState); 138 stateMachine.start(); 139 assertFalse(stateMachine.toString().contains("\n")); 140 } 141 142 /** 143 * Tests {@link StateMachine#quit()}. 144 */ 145 class StateMachineQuitTest extends StateMachine { 146 Collection<LogRec> mLogRecs; 147 148 StateMachineQuitTest(String name) { 149 super(name); 150 mThisSm = this; 151 setDbg(DBG); 152 153 // Setup state machine with 1 state 154 addState(mS1); 155 156 // Set the initial state 157 setInitialState(mS1); 158 } 159 160 @Override 161 public void onQuitting() { 162 tlog("onQuitting"); 163 addLogRec(ON_QUITTING); 164 mLogRecs = mThisSm.copyLogRecs(); 165 synchronized (mThisSm) { 166 mThisSm.notifyAll(); 167 } 168 } 169 170 class S1 extends State { 171 @Override 172 public void exit() { 173 tlog("S1.exit"); 174 addLogRec(EXIT); 175 } 176 @Override 177 public boolean processMessage(Message message) { 178 switch(message.what) { 179 // Sleep and assume the other messages will be queued up. 180 case TEST_CMD_1: { 181 tlog("TEST_CMD_1"); 182 sleep(500); 183 quit(); 184 break; 185 } 186 default: { 187 tlog("default what=" + message.what); 188 break; 189 } 190 } 191 return HANDLED; 192 } 193 } 194 195 private StateMachineQuitTest mThisSm; 196 private S1 mS1 = new S1(); 197 } 198 199 @SmallTest 200 public void testStateMachineQuit() throws Exception { 201 if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); 202 203 StateMachineQuitTest smQuitTest = new StateMachineQuitTest("smQuitTest"); 204 smQuitTest.start(); 205 if (smQuitTest.isDbg()) tlog("testStateMachineQuit E"); 206 207 synchronized (smQuitTest) { 208 209 // Send 6 message we'll quit on the first but all 6 should be processed before quitting. 210 for (int i = 1; i <= 6; i++) { 211 smQuitTest.sendMessage(smQuitTest.obtainMessage(i)); 212 } 213 214 try { 215 // wait for the messages to be handled 216 smQuitTest.wait(); 217 } catch (InterruptedException e) { 218 tloge("testStateMachineQuit: exception while waiting " + e.getMessage()); 219 } 220 } 221 222 dumpLogRecs(smQuitTest.mLogRecs); 223 assertEquals(8, smQuitTest.mLogRecs.size()); 224 225 LogRec lr; 226 Iterator<LogRec> itr = smQuitTest.mLogRecs.iterator(); 227 for (int i = 1; i <= 6; i++) { 228 lr = itr.next(); 229 assertEquals(i, lr.getWhat()); 230 assertEquals(smQuitTest.mS1, lr.getState()); 231 assertEquals(smQuitTest.mS1, lr.getOriginalState()); 232 } 233 lr = itr.next(); 234 assertEquals(EXIT, lr.getInfo()); 235 assertEquals(smQuitTest.mS1, lr.getState()); 236 237 lr = itr.next(); 238 assertEquals(ON_QUITTING, lr.getInfo()); 239 240 if (smQuitTest.isDbg()) tlog("testStateMachineQuit X"); 241 } 242 243 /** 244 * Tests {@link StateMachine#quitNow()} 245 */ 246 class StateMachineQuitNowTest extends StateMachine { 247 public Collection<LogRec> mLogRecs = null; 248 249 StateMachineQuitNowTest(String name) { 250 super(name); 251 mThisSm = this; 252 setDbg(DBG); 253 254 // Setup state machine with 1 state 255 addState(mS1); 256 257 // Set the initial state 258 setInitialState(mS1); 259 } 260 261 @Override 262 public void onQuitting() { 263 tlog("onQuitting"); 264 addLogRec(ON_QUITTING); 265 // Get a copy of the log records since we're quitting and they will disappear 266 mLogRecs = mThisSm.copyLogRecs(); 267 268 synchronized (mThisSm) { 269 mThisSm.notifyAll(); 270 } 271 } 272 273 class S1 extends State { 274 @Override 275 public void exit() { 276 tlog("S1.exit"); 277 addLogRec(EXIT); 278 } 279 @Override 280 public boolean processMessage(Message message) { 281 switch(message.what) { 282 // Sleep and assume the other messages will be queued up. 283 case TEST_CMD_1: { 284 tlog("TEST_CMD_1"); 285 sleep(500); 286 quitNow(); 287 break; 288 } 289 default: { 290 tlog("default what=" + message.what); 291 break; 292 } 293 } 294 return HANDLED; 295 } 296 } 297 298 private StateMachineQuitNowTest mThisSm; 299 private S1 mS1 = new S1(); 300 } 301 302 @SmallTest 303 public void testStateMachineQuitNow() throws Exception { 304 if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); 305 306 StateMachineQuitNowTest smQuitNowTest = new StateMachineQuitNowTest("smQuitNowTest"); 307 smQuitNowTest.start(); 308 if (smQuitNowTest.isDbg()) tlog("testStateMachineQuitNow E"); 309 310 synchronized (smQuitNowTest) { 311 312 // Send 6 message we'll QuitNow on the first even though 313 // we send 6 only one will be processed. 314 for (int i = 1; i <= 6; i++) { 315 smQuitNowTest.sendMessage(smQuitNowTest.obtainMessage(i)); 316 } 317 318 try { 319 // wait for the messages to be handled 320 smQuitNowTest.wait(); 321 } catch (InterruptedException e) { 322 tloge("testStateMachineQuitNow: exception while waiting " + e.getMessage()); 323 } 324 } 325 326 tlog("testStateMachineQuiteNow: logRecs=" + smQuitNowTest.mLogRecs); 327 assertEquals(3, smQuitNowTest.mLogRecs.size()); 328 329 Iterator<LogRec> itr = smQuitNowTest.mLogRecs.iterator(); 330 LogRec lr = itr.next(); 331 assertEquals(1, lr.getWhat()); 332 assertEquals(smQuitNowTest.mS1, lr.getState()); 333 assertEquals(smQuitNowTest.mS1, lr.getOriginalState()); 334 335 lr = itr.next(); 336 assertEquals(EXIT, lr.getInfo()); 337 assertEquals(smQuitNowTest.mS1, lr.getState()); 338 339 lr = itr.next(); 340 assertEquals(ON_QUITTING, lr.getInfo()); 341 342 if (smQuitNowTest.isDbg()) tlog("testStateMachineQuitNow X"); 343 } 344 345 /** 346 * Test enter/exit can use transitionTo 347 */ 348 class StateMachineEnterExitTransitionToTest extends StateMachine { 349 350 StateMachineEnterExitTransitionToTest(String name) { 351 super(name); 352 mThisSm = this; 353 setDbg(DBG); 354 355 // Setup state machine with 1 state 356 addState(mS1); 357 addState(mS2); 358 addState(mS3); 359 addState(mS4); 360 361 // Set the initial state 362 setInitialState(mS1); 363 } 364 365 class S1 extends State { 366 @Override 367 public void enter() { 368 // Test transitions in enter on the initial state work 369 addLogRec(ENTER); 370 transitionTo(mS2); 371 tlog("S1.enter"); 372 } 373 @Override 374 public void exit() { 375 addLogRec(EXIT); 376 tlog("S1.exit"); 377 } 378 } 379 380 class S2 extends State { 381 @Override 382 public void enter() { 383 addLogRec(ENTER); 384 tlog("S2.enter"); 385 } 386 @Override 387 public void exit() { 388 // Test transition in exit work 389 transitionTo(mS4); 390 391 assertEquals(TEST_CMD_1, getCurrentMessage().what); 392 addLogRec(EXIT); 393 394 tlog("S2.exit"); 395 } 396 @Override 397 public boolean processMessage(Message message) { 398 // Start a transition to S3 but it will be 399 // changed to a transition to S4 in exit 400 transitionTo(mS3); 401 tlog("S2.processMessage"); 402 return HANDLED; 403 } 404 } 405 406 class S3 extends State { 407 @Override 408 public void enter() { 409 addLogRec(ENTER); 410 tlog("S3.enter"); 411 } 412 @Override 413 public void exit() { 414 addLogRec(EXIT); 415 tlog("S3.exit"); 416 } 417 } 418 419 class S4 extends State { 420 @Override 421 public void enter() { 422 addLogRec(ENTER); 423 // Test that we can do halting in an enter/exit 424 transitionToHaltingState(); 425 tlog("S4.enter"); 426 } 427 @Override 428 public void exit() { 429 addLogRec(EXIT); 430 tlog("S4.exit"); 431 } 432 } 433 434 @Override 435 protected void onHalting() { 436 synchronized (mThisSm) { 437 mThisSm.notifyAll(); 438 } 439 } 440 441 private StateMachineEnterExitTransitionToTest mThisSm; 442 private S1 mS1 = new S1(); 443 private S2 mS2 = new S2(); 444 private S3 mS3 = new S3(); 445 private S4 mS4 = new S4(); 446 } 447 448 @SmallTest 449 public void testStateMachineEnterExitTransitionToTest() throws Exception { 450 //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); 451 452 StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest = 453 new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest"); 454 smEnterExitTranstionToTest.start(); 455 if (smEnterExitTranstionToTest.isDbg()) { 456 tlog("testStateMachineEnterExitTransitionToTest E"); 457 } 458 459 synchronized (smEnterExitTranstionToTest) { 460 smEnterExitTranstionToTest.sendMessage(TEST_CMD_1); 461 462 try { 463 // wait for the messages to be handled 464 smEnterExitTranstionToTest.wait(); 465 } catch (InterruptedException e) { 466 tloge("testStateMachineEnterExitTransitionToTest: exception while waiting " 467 + e.getMessage()); 468 } 469 } 470 471 dumpLogRecs(smEnterExitTranstionToTest); 472 473 assertEquals(9, smEnterExitTranstionToTest.getLogRecCount()); 474 LogRec lr; 475 476 lr = smEnterExitTranstionToTest.getLogRec(0); 477 assertEquals(ENTER, lr.getInfo()); 478 assertEquals(smEnterExitTranstionToTest.mS1, lr.getState()); 479 480 lr = smEnterExitTranstionToTest.getLogRec(1); 481 assertEquals(EXIT, lr.getInfo()); 482 assertEquals(smEnterExitTranstionToTest.mS1, lr.getState()); 483 484 lr = smEnterExitTranstionToTest.getLogRec(2); 485 assertEquals(ENTER, lr.getInfo()); 486 assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); 487 488 lr = smEnterExitTranstionToTest.getLogRec(3); 489 assertEquals(TEST_CMD_1, lr.getWhat()); 490 assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); 491 assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState()); 492 assertEquals(smEnterExitTranstionToTest.mS3, lr.getDestState()); 493 494 lr = smEnterExitTranstionToTest.getLogRec(4); 495 assertEquals(TEST_CMD_1, lr.getWhat()); 496 assertEquals(smEnterExitTranstionToTest.mS2, lr.getState()); 497 assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState()); 498 assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); 499 assertEquals(EXIT, lr.getInfo()); 500 501 lr = smEnterExitTranstionToTest.getLogRec(5); 502 assertEquals(TEST_CMD_1, lr.getWhat()); 503 assertEquals(ENTER, lr.getInfo()); 504 assertEquals(smEnterExitTranstionToTest.mS3, lr.getState()); 505 assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState()); 506 assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); 507 508 lr = smEnterExitTranstionToTest.getLogRec(6); 509 assertEquals(TEST_CMD_1, lr.getWhat()); 510 assertEquals(EXIT, lr.getInfo()); 511 assertEquals(smEnterExitTranstionToTest.mS3, lr.getState()); 512 assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState()); 513 assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); 514 515 lr = smEnterExitTranstionToTest.getLogRec(7); 516 assertEquals(TEST_CMD_1, lr.getWhat()); 517 assertEquals(ENTER, lr.getInfo()); 518 assertEquals(smEnterExitTranstionToTest.mS4, lr.getState()); 519 assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState()); 520 assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState()); 521 522 lr = smEnterExitTranstionToTest.getLogRec(8); 523 assertEquals(TEST_CMD_1, lr.getWhat()); 524 assertEquals(EXIT, lr.getInfo()); 525 assertEquals(smEnterExitTranstionToTest.mS4, lr.getState()); 526 assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState()); 527 528 if (smEnterExitTranstionToTest.isDbg()) { 529 tlog("testStateMachineEnterExitTransitionToTest X"); 530 } 531 } 532 533 /** 534 * Tests that ProcessedMessage works as a circular buffer. 535 */ 536 class StateMachine0 extends StateMachine { 537 StateMachine0(String name) { 538 super(name); 539 mThisSm = this; 540 setDbg(DBG); 541 setLogRecSize(3); 542 543 // Setup state machine with 1 state 544 addState(mS1); 545 546 // Set the initial state 547 setInitialState(mS1); 548 } 549 550 class S1 extends State { 551 @Override 552 public boolean processMessage(Message message) { 553 if (message.what == TEST_CMD_6) { 554 transitionToHaltingState(); 555 } 556 return HANDLED; 557 } 558 } 559 560 @Override 561 protected void onHalting() { 562 synchronized (mThisSm) { 563 mThisSm.notifyAll(); 564 } 565 } 566 567 private StateMachine0 mThisSm; 568 private S1 mS1 = new S1(); 569 } 570 571 @SmallTest 572 public void testStateMachine0() throws Exception { 573 //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger(); 574 575 StateMachine0 sm0 = new StateMachine0("sm0"); 576 sm0.start(); 577 if (sm0.isDbg()) tlog("testStateMachine0 E"); 578 579 synchronized (sm0) { 580 // Send 6 messages 581 for (int i = 1; i <= 6; i++) { 582 sm0.sendMessage(sm0.obtainMessage(i)); 583 } 584 585 try { 586 // wait for the messages to be handled 587 sm0.wait(); 588 } catch (InterruptedException e) { 589 tloge("testStateMachine0: exception while waiting " + e.getMessage()); 590 } 591 } 592 593 assertEquals(6, sm0.getLogRecCount()); 594 assertEquals(3, sm0.getLogRecSize()); 595 596 dumpLogRecs(sm0); 597 598 LogRec lr; 599 lr = sm0.getLogRec(0); 600 assertEquals(TEST_CMD_4, lr.getWhat()); 601 assertEquals(sm0.mS1, lr.getState()); 602 assertEquals(sm0.mS1, lr.getOriginalState()); 603 604 lr = sm0.getLogRec(1); 605 assertEquals(TEST_CMD_5, lr.getWhat()); 606 assertEquals(sm0.mS1, lr.getState()); 607 assertEquals(sm0.mS1, lr.getOriginalState()); 608 609 lr = sm0.getLogRec(2); 610 assertEquals(TEST_CMD_6, lr.getWhat()); 611 assertEquals(sm0.mS1, lr.getState()); 612 assertEquals(sm0.mS1, lr.getOriginalState()); 613 614 if (sm0.isDbg()) tlog("testStateMachine0 X"); 615 } 616 617 /** 618 * This tests enter/exit and transitions to the same state. 619 * The state machine has one state, it receives two messages 620 * in state mS1. With the first message it transitions to 621 * itself which causes it to be exited and reentered. 622 */ 623 class StateMachine1 extends StateMachine { 624 StateMachine1(String name) { 625 super(name); 626 mThisSm = this; 627 setDbg(DBG); 628 629 // Setup state machine with 1 state 630 addState(mS1); 631 632 // Set the initial state 633 setInitialState(mS1); 634 if (DBG) tlog("StateMachine1: ctor X"); 635 } 636 637 class S1 extends State { 638 @Override 639 public void enter() { 640 mEnterCount++; 641 } 642 @Override 643 public void exit() { 644 mExitCount++; 645 } 646 @Override 647 public boolean processMessage(Message message) { 648 if (message.what == TEST_CMD_1) { 649 assertEquals(1, mEnterCount); 650 assertEquals(0, mExitCount); 651 transitionTo(mS1); 652 } else if (message.what == TEST_CMD_2) { 653 assertEquals(2, mEnterCount); 654 assertEquals(1, mExitCount); 655 transitionToHaltingState(); 656 } 657 return HANDLED; 658 } 659 } 660 661 @Override 662 protected void onHalting() { 663 synchronized (mThisSm) { 664 mThisSm.notifyAll(); 665 } 666 } 667 668 private StateMachine1 mThisSm; 669 private S1 mS1 = new S1(); 670 671 private int mEnterCount; 672 private int mExitCount; 673 } 674 675 @MediumTest 676 public void testStateMachine1() throws Exception { 677 StateMachine1 sm1 = new StateMachine1("sm1"); 678 sm1.start(); 679 if (sm1.isDbg()) tlog("testStateMachine1 E"); 680 681 synchronized (sm1) { 682 // Send two messages 683 sm1.sendMessage(TEST_CMD_1); 684 sm1.sendMessage(TEST_CMD_2); 685 686 try { 687 // wait for the messages to be handled 688 sm1.wait(); 689 } catch (InterruptedException e) { 690 tloge("testStateMachine1: exception while waiting " + e.getMessage()); 691 } 692 } 693 694 assertEquals(2, sm1.mEnterCount); 695 assertEquals(2, sm1.mExitCount); 696 697 assertEquals(2, sm1.getLogRecSize()); 698 699 LogRec lr; 700 lr = sm1.getLogRec(0); 701 assertEquals(TEST_CMD_1, lr.getWhat()); 702 assertEquals(sm1.mS1, lr.getState()); 703 assertEquals(sm1.mS1, lr.getOriginalState()); 704 705 lr = sm1.getLogRec(1); 706 assertEquals(TEST_CMD_2, lr.getWhat()); 707 assertEquals(sm1.mS1, lr.getState()); 708 assertEquals(sm1.mS1, lr.getOriginalState()); 709 710 assertEquals(2, sm1.mEnterCount); 711 assertEquals(2, sm1.mExitCount); 712 713 if (sm1.isDbg()) tlog("testStateMachine1 X"); 714 } 715 716 /** 717 * Test deferring messages and states with no parents. The state machine 718 * has two states, it receives two messages in state mS1 deferring them 719 * until what == TEST_CMD_2 and then transitions to state mS2. State 720 * mS2 then receives both of the deferred messages first TEST_CMD_1 and 721 * then TEST_CMD_2. 722 */ 723 class StateMachine2 extends StateMachine { 724 StateMachine2(String name) { 725 super(name); 726 mThisSm = this; 727 setDbg(DBG); 728 729 // Setup the hierarchy 730 addState(mS1); 731 addState(mS2); 732 733 // Set the initial state 734 setInitialState(mS1); 735 if (DBG) tlog("StateMachine2: ctor X"); 736 } 737 738 class S1 extends State { 739 @Override 740 public void enter() { 741 mDidEnter = true; 742 } 743 @Override 744 public void exit() { 745 mDidExit = true; 746 } 747 @Override 748 public boolean processMessage(Message message) { 749 deferMessage(message); 750 if (message.what == TEST_CMD_2) { 751 transitionTo(mS2); 752 } 753 return HANDLED; 754 } 755 } 756 757 class S2 extends State { 758 @Override 759 public boolean processMessage(Message message) { 760 if (message.what == TEST_CMD_2) { 761 transitionToHaltingState(); 762 } 763 return HANDLED; 764 } 765 } 766 767 @Override 768 protected void onHalting() { 769 synchronized (mThisSm) { 770 mThisSm.notifyAll(); 771 } 772 } 773 774 private StateMachine2 mThisSm; 775 private S1 mS1 = new S1(); 776 private S2 mS2 = new S2(); 777 778 private boolean mDidEnter = false; 779 private boolean mDidExit = false; 780 } 781 782 @MediumTest 783 public void testStateMachine2() throws Exception { 784 StateMachine2 sm2 = new StateMachine2("sm2"); 785 sm2.start(); 786 if (sm2.isDbg()) tlog("testStateMachine2 E"); 787 788 synchronized (sm2) { 789 // Send two messages 790 sm2.sendMessage(TEST_CMD_1); 791 sm2.sendMessage(TEST_CMD_2); 792 793 try { 794 // wait for the messages to be handled 795 sm2.wait(); 796 } catch (InterruptedException e) { 797 tloge("testStateMachine2: exception while waiting " + e.getMessage()); 798 } 799 } 800 801 assertEquals(4, sm2.getLogRecSize()); 802 803 LogRec lr; 804 lr = sm2.getLogRec(0); 805 assertEquals(TEST_CMD_1, lr.getWhat()); 806 assertEquals(sm2.mS1, lr.getState()); 807 808 lr = sm2.getLogRec(1); 809 assertEquals(TEST_CMD_2, lr.getWhat()); 810 assertEquals(sm2.mS1, lr.getState()); 811 812 lr = sm2.getLogRec(2); 813 assertEquals(TEST_CMD_1, lr.getWhat()); 814 assertEquals(sm2.mS2, lr.getState()); 815 816 lr = sm2.getLogRec(3); 817 assertEquals(TEST_CMD_2, lr.getWhat()); 818 assertEquals(sm2.mS2, lr.getState()); 819 820 assertTrue(sm2.mDidEnter); 821 assertTrue(sm2.mDidExit); 822 823 if (sm2.isDbg()) tlog("testStateMachine2 X"); 824 } 825 826 /** 827 * Test that unhandled messages in a child are handled by the parent. 828 * When TEST_CMD_2 is received. 829 */ 830 class StateMachine3 extends StateMachine { 831 StateMachine3(String name) { 832 super(name); 833 mThisSm = this; 834 setDbg(DBG); 835 836 // Setup the simplest hierarchy of two states 837 // mParentState and mChildState. 838 // (Use indentation to help visualize hierarchy) 839 addState(mParentState); 840 addState(mChildState, mParentState); 841 842 // Set the initial state will be the child 843 setInitialState(mChildState); 844 if (DBG) tlog("StateMachine3: ctor X"); 845 } 846 847 class ParentState extends State { 848 @Override 849 public boolean processMessage(Message message) { 850 if (message.what == TEST_CMD_2) { 851 transitionToHaltingState(); 852 } 853 return HANDLED; 854 } 855 } 856 857 class ChildState extends State { 858 @Override 859 public boolean processMessage(Message message) { 860 return NOT_HANDLED; 861 } 862 } 863 864 @Override 865 protected void onHalting() { 866 synchronized (mThisSm) { 867 mThisSm.notifyAll(); 868 } 869 } 870 871 private StateMachine3 mThisSm; 872 private ParentState mParentState = new ParentState(); 873 private ChildState mChildState = new ChildState(); 874 } 875 876 @MediumTest 877 public void testStateMachine3() throws Exception { 878 StateMachine3 sm3 = new StateMachine3("sm3"); 879 sm3.start(); 880 if (sm3.isDbg()) tlog("testStateMachine3 E"); 881 882 synchronized (sm3) { 883 // Send two messages 884 sm3.sendMessage(TEST_CMD_1); 885 sm3.sendMessage(TEST_CMD_2); 886 887 try { 888 // wait for the messages to be handled 889 sm3.wait(); 890 } catch (InterruptedException e) { 891 tloge("testStateMachine3: exception while waiting " + e.getMessage()); 892 } 893 } 894 895 assertEquals(2, sm3.getLogRecSize()); 896 897 LogRec lr; 898 lr = sm3.getLogRec(0); 899 assertEquals(TEST_CMD_1, lr.getWhat()); 900 assertEquals(sm3.mParentState, lr.getState()); 901 assertEquals(sm3.mChildState, lr.getOriginalState()); 902 903 lr = sm3.getLogRec(1); 904 assertEquals(TEST_CMD_2, lr.getWhat()); 905 assertEquals(sm3.mParentState, lr.getState()); 906 assertEquals(sm3.mChildState, lr.getOriginalState()); 907 908 if (sm3.isDbg()) tlog("testStateMachine3 X"); 909 } 910 911 /** 912 * Test a hierarchy of 3 states a parent and two children 913 * with transition from child 1 to child 2 and child 2 914 * lets the parent handle the messages. 915 */ 916 class StateMachine4 extends StateMachine { 917 StateMachine4(String name) { 918 super(name); 919 mThisSm = this; 920 setDbg(DBG); 921 922 // Setup a hierarchy of three states 923 // mParentState, mChildState1 & mChildState2 924 // (Use indentation to help visualize hierarchy) 925 addState(mParentState); 926 addState(mChildState1, mParentState); 927 addState(mChildState2, mParentState); 928 929 // Set the initial state will be child 1 930 setInitialState(mChildState1); 931 if (DBG) tlog("StateMachine4: ctor X"); 932 } 933 934 class ParentState extends State { 935 @Override 936 public boolean processMessage(Message message) { 937 if (message.what == TEST_CMD_2) { 938 transitionToHaltingState(); 939 } 940 return HANDLED; 941 } 942 } 943 944 class ChildState1 extends State { 945 @Override 946 public boolean processMessage(Message message) { 947 transitionTo(mChildState2); 948 return HANDLED; 949 } 950 } 951 952 class ChildState2 extends State { 953 @Override 954 public boolean processMessage(Message message) { 955 return NOT_HANDLED; 956 } 957 } 958 959 @Override 960 protected void onHalting() { 961 synchronized (mThisSm) { 962 mThisSm.notifyAll(); 963 } 964 } 965 966 private StateMachine4 mThisSm; 967 private ParentState mParentState = new ParentState(); 968 private ChildState1 mChildState1 = new ChildState1(); 969 private ChildState2 mChildState2 = new ChildState2(); 970 } 971 972 @MediumTest 973 public void testStateMachine4() throws Exception { 974 StateMachine4 sm4 = new StateMachine4("sm4"); 975 sm4.start(); 976 if (sm4.isDbg()) tlog("testStateMachine4 E"); 977 978 synchronized (sm4) { 979 // Send two messages 980 sm4.sendMessage(TEST_CMD_1); 981 sm4.sendMessage(TEST_CMD_2); 982 983 try { 984 // wait for the messages to be handled 985 sm4.wait(); 986 } catch (InterruptedException e) { 987 tloge("testStateMachine4: exception while waiting " + e.getMessage()); 988 } 989 } 990 991 992 assertEquals(2, sm4.getLogRecSize()); 993 994 LogRec lr; 995 lr = sm4.getLogRec(0); 996 assertEquals(TEST_CMD_1, lr.getWhat()); 997 assertEquals(sm4.mChildState1, lr.getState()); 998 assertEquals(sm4.mChildState1, lr.getOriginalState()); 999 1000 lr = sm4.getLogRec(1); 1001 assertEquals(TEST_CMD_2, lr.getWhat()); 1002 assertEquals(sm4.mParentState, lr.getState()); 1003 assertEquals(sm4.mChildState2, lr.getOriginalState()); 1004 1005 if (sm4.isDbg()) tlog("testStateMachine4 X"); 1006 } 1007 1008 /** 1009 * Test transition from one child to another of a "complex" 1010 * hierarchy with two parents and multiple children. 1011 */ 1012 class StateMachine5 extends StateMachine { 1013 StateMachine5(String name) { 1014 super(name); 1015 mThisSm = this; 1016 setDbg(DBG); 1017 1018 // Setup a hierarchy with two parents and some children. 1019 // (Use indentation to help visualize hierarchy) 1020 addState(mParentState1); 1021 addState(mChildState1, mParentState1); 1022 addState(mChildState2, mParentState1); 1023 1024 addState(mParentState2); 1025 addState(mChildState3, mParentState2); 1026 addState(mChildState4, mParentState2); 1027 addState(mChildState5, mChildState4); 1028 1029 // Set the initial state will be the child 1030 setInitialState(mChildState1); 1031 if (DBG) tlog("StateMachine5: ctor X"); 1032 } 1033 1034 class ParentState1 extends State { 1035 @Override 1036 public void enter() { 1037 mParentState1EnterCount += 1; 1038 } 1039 @Override 1040 public void exit() { 1041 mParentState1ExitCount += 1; 1042 } 1043 @Override 1044 public boolean processMessage(Message message) { 1045 return HANDLED; 1046 } 1047 } 1048 1049 class ChildState1 extends State { 1050 @Override 1051 public void enter() { 1052 mChildState1EnterCount += 1; 1053 } 1054 @Override 1055 public void exit() { 1056 mChildState1ExitCount += 1; 1057 } 1058 @Override 1059 public boolean processMessage(Message message) { 1060 assertEquals(1, mParentState1EnterCount); 1061 assertEquals(0, mParentState1ExitCount); 1062 assertEquals(1, mChildState1EnterCount); 1063 assertEquals(0, mChildState1ExitCount); 1064 assertEquals(0, mChildState2EnterCount); 1065 assertEquals(0, mChildState2ExitCount); 1066 assertEquals(0, mParentState2EnterCount); 1067 assertEquals(0, mParentState2ExitCount); 1068 assertEquals(0, mChildState3EnterCount); 1069 assertEquals(0, mChildState3ExitCount); 1070 assertEquals(0, mChildState4EnterCount); 1071 assertEquals(0, mChildState4ExitCount); 1072 assertEquals(0, mChildState5EnterCount); 1073 assertEquals(0, mChildState5ExitCount); 1074 1075 transitionTo(mChildState2); 1076 return HANDLED; 1077 } 1078 } 1079 1080 class ChildState2 extends State { 1081 @Override 1082 public void enter() { 1083 mChildState2EnterCount += 1; 1084 } 1085 @Override 1086 public void exit() { 1087 mChildState2ExitCount += 1; 1088 } 1089 @Override 1090 public boolean processMessage(Message message) { 1091 assertEquals(1, mParentState1EnterCount); 1092 assertEquals(0, mParentState1ExitCount); 1093 assertEquals(1, mChildState1EnterCount); 1094 assertEquals(1, mChildState1ExitCount); 1095 assertEquals(1, mChildState2EnterCount); 1096 assertEquals(0, mChildState2ExitCount); 1097 assertEquals(0, mParentState2EnterCount); 1098 assertEquals(0, mParentState2ExitCount); 1099 assertEquals(0, mChildState3EnterCount); 1100 assertEquals(0, mChildState3ExitCount); 1101 assertEquals(0, mChildState4EnterCount); 1102 assertEquals(0, mChildState4ExitCount); 1103 assertEquals(0, mChildState5EnterCount); 1104 assertEquals(0, mChildState5ExitCount); 1105 1106 transitionTo(mChildState5); 1107 return HANDLED; 1108 } 1109 } 1110 1111 class ParentState2 extends State { 1112 @Override 1113 public void enter() { 1114 mParentState2EnterCount += 1; 1115 } 1116 @Override 1117 public void exit() { 1118 mParentState2ExitCount += 1; 1119 } 1120 @Override 1121 public boolean processMessage(Message message) { 1122 assertEquals(1, mParentState1EnterCount); 1123 assertEquals(1, mParentState1ExitCount); 1124 assertEquals(1, mChildState1EnterCount); 1125 assertEquals(1, mChildState1ExitCount); 1126 assertEquals(1, mChildState2EnterCount); 1127 assertEquals(1, mChildState2ExitCount); 1128 assertEquals(2, mParentState2EnterCount); 1129 assertEquals(1, mParentState2ExitCount); 1130 assertEquals(1, mChildState3EnterCount); 1131 assertEquals(1, mChildState3ExitCount); 1132 assertEquals(2, mChildState4EnterCount); 1133 assertEquals(2, mChildState4ExitCount); 1134 assertEquals(1, mChildState5EnterCount); 1135 assertEquals(1, mChildState5ExitCount); 1136 1137 transitionToHaltingState(); 1138 return HANDLED; 1139 } 1140 } 1141 1142 class ChildState3 extends State { 1143 @Override 1144 public void enter() { 1145 mChildState3EnterCount += 1; 1146 } 1147 @Override 1148 public void exit() { 1149 mChildState3ExitCount += 1; 1150 } 1151 @Override 1152 public boolean processMessage(Message message) { 1153 assertEquals(1, mParentState1EnterCount); 1154 assertEquals(1, mParentState1ExitCount); 1155 assertEquals(1, mChildState1EnterCount); 1156 assertEquals(1, mChildState1ExitCount); 1157 assertEquals(1, mChildState2EnterCount); 1158 assertEquals(1, mChildState2ExitCount); 1159 assertEquals(1, mParentState2EnterCount); 1160 assertEquals(0, mParentState2ExitCount); 1161 assertEquals(1, mChildState3EnterCount); 1162 assertEquals(0, mChildState3ExitCount); 1163 assertEquals(1, mChildState4EnterCount); 1164 assertEquals(1, mChildState4ExitCount); 1165 assertEquals(1, mChildState5EnterCount); 1166 assertEquals(1, mChildState5ExitCount); 1167 1168 transitionTo(mChildState4); 1169 return HANDLED; 1170 } 1171 } 1172 1173 class ChildState4 extends State { 1174 @Override 1175 public void enter() { 1176 mChildState4EnterCount += 1; 1177 } 1178 @Override 1179 public void exit() { 1180 mChildState4ExitCount += 1; 1181 } 1182 @Override 1183 public boolean processMessage(Message message) { 1184 assertEquals(1, mParentState1EnterCount); 1185 assertEquals(1, mParentState1ExitCount); 1186 assertEquals(1, mChildState1EnterCount); 1187 assertEquals(1, mChildState1ExitCount); 1188 assertEquals(1, mChildState2EnterCount); 1189 assertEquals(1, mChildState2ExitCount); 1190 assertEquals(1, mParentState2EnterCount); 1191 assertEquals(0, mParentState2ExitCount); 1192 assertEquals(1, mChildState3EnterCount); 1193 assertEquals(1, mChildState3ExitCount); 1194 assertEquals(2, mChildState4EnterCount); 1195 assertEquals(1, mChildState4ExitCount); 1196 assertEquals(1, mChildState5EnterCount); 1197 assertEquals(1, mChildState5ExitCount); 1198 1199 transitionTo(mParentState2); 1200 return HANDLED; 1201 } 1202 } 1203 1204 class ChildState5 extends State { 1205 @Override 1206 public void enter() { 1207 mChildState5EnterCount += 1; 1208 } 1209 @Override 1210 public void exit() { 1211 mChildState5ExitCount += 1; 1212 } 1213 @Override 1214 public boolean processMessage(Message message) { 1215 assertEquals(1, mParentState1EnterCount); 1216 assertEquals(1, mParentState1ExitCount); 1217 assertEquals(1, mChildState1EnterCount); 1218 assertEquals(1, mChildState1ExitCount); 1219 assertEquals(1, mChildState2EnterCount); 1220 assertEquals(1, mChildState2ExitCount); 1221 assertEquals(1, mParentState2EnterCount); 1222 assertEquals(0, mParentState2ExitCount); 1223 assertEquals(0, mChildState3EnterCount); 1224 assertEquals(0, mChildState3ExitCount); 1225 assertEquals(1, mChildState4EnterCount); 1226 assertEquals(0, mChildState4ExitCount); 1227 assertEquals(1, mChildState5EnterCount); 1228 assertEquals(0, mChildState5ExitCount); 1229 1230 transitionTo(mChildState3); 1231 return HANDLED; 1232 } 1233 } 1234 1235 @Override 1236 protected void onHalting() { 1237 synchronized (mThisSm) { 1238 mThisSm.notifyAll(); 1239 } 1240 } 1241 1242 private StateMachine5 mThisSm; 1243 private ParentState1 mParentState1 = new ParentState1(); 1244 private ChildState1 mChildState1 = new ChildState1(); 1245 private ChildState2 mChildState2 = new ChildState2(); 1246 private ParentState2 mParentState2 = new ParentState2(); 1247 private ChildState3 mChildState3 = new ChildState3(); 1248 private ChildState4 mChildState4 = new ChildState4(); 1249 private ChildState5 mChildState5 = new ChildState5(); 1250 1251 private int mParentState1EnterCount = 0; 1252 private int mParentState1ExitCount = 0; 1253 private int mChildState1EnterCount = 0; 1254 private int mChildState1ExitCount = 0; 1255 private int mChildState2EnterCount = 0; 1256 private int mChildState2ExitCount = 0; 1257 private int mParentState2EnterCount = 0; 1258 private int mParentState2ExitCount = 0; 1259 private int mChildState3EnterCount = 0; 1260 private int mChildState3ExitCount = 0; 1261 private int mChildState4EnterCount = 0; 1262 private int mChildState4ExitCount = 0; 1263 private int mChildState5EnterCount = 0; 1264 private int mChildState5ExitCount = 0; 1265 } 1266 1267 @MediumTest 1268 public void testStateMachine5() throws Exception { 1269 StateMachine5 sm5 = new StateMachine5("sm5"); 1270 sm5.start(); 1271 if (sm5.isDbg()) tlog("testStateMachine5 E"); 1272 1273 synchronized (sm5) { 1274 // Send 6 messages 1275 sm5.sendMessage(TEST_CMD_1); 1276 sm5.sendMessage(TEST_CMD_2); 1277 sm5.sendMessage(TEST_CMD_3); 1278 sm5.sendMessage(TEST_CMD_4); 1279 sm5.sendMessage(TEST_CMD_5); 1280 sm5.sendMessage(TEST_CMD_6); 1281 1282 try { 1283 // wait for the messages to be handled 1284 sm5.wait(); 1285 } catch (InterruptedException e) { 1286 tloge("testStateMachine5: exception while waiting " + e.getMessage()); 1287 } 1288 } 1289 1290 1291 assertEquals(6, sm5.getLogRecSize()); 1292 1293 assertEquals(1, sm5.mParentState1EnterCount); 1294 assertEquals(1, sm5.mParentState1ExitCount); 1295 assertEquals(1, sm5.mChildState1EnterCount); 1296 assertEquals(1, sm5.mChildState1ExitCount); 1297 assertEquals(1, sm5.mChildState2EnterCount); 1298 assertEquals(1, sm5.mChildState2ExitCount); 1299 assertEquals(2, sm5.mParentState2EnterCount); 1300 assertEquals(2, sm5.mParentState2ExitCount); 1301 assertEquals(1, sm5.mChildState3EnterCount); 1302 assertEquals(1, sm5.mChildState3ExitCount); 1303 assertEquals(2, sm5.mChildState4EnterCount); 1304 assertEquals(2, sm5.mChildState4ExitCount); 1305 assertEquals(1, sm5.mChildState5EnterCount); 1306 assertEquals(1, sm5.mChildState5ExitCount); 1307 1308 LogRec lr; 1309 lr = sm5.getLogRec(0); 1310 assertEquals(TEST_CMD_1, lr.getWhat()); 1311 assertEquals(sm5.mChildState1, lr.getState()); 1312 assertEquals(sm5.mChildState1, lr.getOriginalState()); 1313 1314 lr = sm5.getLogRec(1); 1315 assertEquals(TEST_CMD_2, lr.getWhat()); 1316 assertEquals(sm5.mChildState2, lr.getState()); 1317 assertEquals(sm5.mChildState2, lr.getOriginalState()); 1318 1319 lr = sm5.getLogRec(2); 1320 assertEquals(TEST_CMD_3, lr.getWhat()); 1321 assertEquals(sm5.mChildState5, lr.getState()); 1322 assertEquals(sm5.mChildState5, lr.getOriginalState()); 1323 1324 lr = sm5.getLogRec(3); 1325 assertEquals(TEST_CMD_4, lr.getWhat()); 1326 assertEquals(sm5.mChildState3, lr.getState()); 1327 assertEquals(sm5.mChildState3, lr.getOriginalState()); 1328 1329 lr = sm5.getLogRec(4); 1330 assertEquals(TEST_CMD_5, lr.getWhat()); 1331 assertEquals(sm5.mChildState4, lr.getState()); 1332 assertEquals(sm5.mChildState4, lr.getOriginalState()); 1333 1334 lr = sm5.getLogRec(5); 1335 assertEquals(TEST_CMD_6, lr.getWhat()); 1336 assertEquals(sm5.mParentState2, lr.getState()); 1337 assertEquals(sm5.mParentState2, lr.getOriginalState()); 1338 1339 if (sm5.isDbg()) tlog("testStateMachine5 X"); 1340 } 1341 1342 /** 1343 * Test that the initial state enter is invoked immediately 1344 * after construction and before any other messages arrive and that 1345 * sendMessageDelayed works. 1346 */ 1347 class StateMachine6 extends StateMachine { 1348 StateMachine6(String name) { 1349 super(name); 1350 mThisSm = this; 1351 setDbg(DBG); 1352 1353 // Setup state machine with 1 state 1354 addState(mS1); 1355 1356 // Set the initial state 1357 setInitialState(mS1); 1358 if (DBG) tlog("StateMachine6: ctor X"); 1359 } 1360 1361 class S1 extends State { 1362 @Override 1363 public void enter() { 1364 sendMessage(TEST_CMD_1); 1365 } 1366 @Override 1367 public boolean processMessage(Message message) { 1368 if (message.what == TEST_CMD_1) { 1369 mArrivalTimeMsg1 = SystemClock.elapsedRealtime(); 1370 } else if (message.what == TEST_CMD_2) { 1371 mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); 1372 transitionToHaltingState(); 1373 } 1374 return HANDLED; 1375 } 1376 } 1377 1378 @Override 1379 protected void onHalting() { 1380 synchronized (mThisSm) { 1381 mThisSm.notifyAll(); 1382 } 1383 } 1384 1385 private StateMachine6 mThisSm; 1386 private S1 mS1 = new S1(); 1387 1388 private long mArrivalTimeMsg1; 1389 private long mArrivalTimeMsg2; 1390 } 1391 1392 @MediumTest 1393 public void testStateMachine6() throws Exception { 1394 final int DELAY_TIME = 250; 1395 final int DELAY_FUDGE = 20; 1396 1397 StateMachine6 sm6 = new StateMachine6("sm6"); 1398 sm6.start(); 1399 if (sm6.isDbg()) tlog("testStateMachine6 E"); 1400 1401 synchronized (sm6) { 1402 // Send a message 1403 sm6.sendMessageDelayed(TEST_CMD_2, DELAY_TIME); 1404 1405 try { 1406 // wait for the messages to be handled 1407 sm6.wait(); 1408 } catch (InterruptedException e) { 1409 tloge("testStateMachine6: exception while waiting " + e.getMessage()); 1410 } 1411 } 1412 1413 /** 1414 * TEST_CMD_1 was sent in enter and must always have been processed 1415 * immediately after construction and hence the arrival time difference 1416 * should always >= to the DELAY_TIME 1417 */ 1418 long arrivalTimeDiff = sm6.mArrivalTimeMsg2 - sm6.mArrivalTimeMsg1; 1419 long expectedDelay = DELAY_TIME - DELAY_FUDGE; 1420 if (sm6.isDbg()) tlog("testStateMachine6: expect " + arrivalTimeDiff 1421 + " >= " + expectedDelay); 1422 assertTrue(arrivalTimeDiff >= expectedDelay); 1423 1424 if (sm6.isDbg()) tlog("testStateMachine6 X"); 1425 } 1426 1427 /** 1428 * Test that enter is invoked immediately after exit. This validates 1429 * that enter can be used to send a watch dog message for its state. 1430 */ 1431 class StateMachine7 extends StateMachine { 1432 private final int SM7_DELAY_TIME = 250; 1433 1434 StateMachine7(String name) { 1435 super(name); 1436 mThisSm = this; 1437 setDbg(DBG); 1438 1439 // Setup state machine with 1 state 1440 addState(mS1); 1441 addState(mS2); 1442 1443 // Set the initial state 1444 setInitialState(mS1); 1445 if (DBG) tlog("StateMachine7: ctor X"); 1446 } 1447 1448 class S1 extends State { 1449 @Override 1450 public void exit() { 1451 sendMessage(TEST_CMD_2); 1452 } 1453 @Override 1454 public boolean processMessage(Message message) { 1455 transitionTo(mS2); 1456 return HANDLED; 1457 } 1458 } 1459 1460 class S2 extends State { 1461 @Override 1462 public void enter() { 1463 // Send a delayed message as a watch dog 1464 sendMessageDelayed(TEST_CMD_3, SM7_DELAY_TIME); 1465 } 1466 @Override 1467 public boolean processMessage(Message message) { 1468 if (message.what == TEST_CMD_2) { 1469 mMsgCount += 1; 1470 mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); 1471 } else if (message.what == TEST_CMD_3) { 1472 mMsgCount += 1; 1473 mArrivalTimeMsg3 = SystemClock.elapsedRealtime(); 1474 } 1475 1476 if (mMsgCount == 2) { 1477 transitionToHaltingState(); 1478 } 1479 return HANDLED; 1480 } 1481 } 1482 1483 @Override 1484 protected void onHalting() { 1485 synchronized (mThisSm) { 1486 mThisSm.notifyAll(); 1487 } 1488 } 1489 1490 private StateMachine7 mThisSm; 1491 private S1 mS1 = new S1(); 1492 private S2 mS2 = new S2(); 1493 1494 private int mMsgCount = 0; 1495 private long mArrivalTimeMsg2; 1496 private long mArrivalTimeMsg3; 1497 } 1498 1499 @MediumTest 1500 public void testStateMachine7() throws Exception { 1501 final int SM7_DELAY_FUDGE = 20; 1502 1503 StateMachine7 sm7 = new StateMachine7("sm7"); 1504 sm7.start(); 1505 if (sm7.isDbg()) tlog("testStateMachine7 E"); 1506 1507 synchronized (sm7) { 1508 // Send a message 1509 sm7.sendMessage(TEST_CMD_1); 1510 1511 try { 1512 // wait for the messages to be handled 1513 sm7.wait(); 1514 } catch (InterruptedException e) { 1515 tloge("testStateMachine7: exception while waiting " + e.getMessage()); 1516 } 1517 } 1518 1519 /** 1520 * TEST_CMD_3 was sent in S2.enter with a delay and must always have been 1521 * processed immediately after S1.exit. Since S1.exit sent TEST_CMD_2 1522 * without a delay the arrival time difference should always >= to SM7_DELAY_TIME. 1523 */ 1524 long arrivalTimeDiff = sm7.mArrivalTimeMsg3 - sm7.mArrivalTimeMsg2; 1525 long expectedDelay = sm7.SM7_DELAY_TIME - SM7_DELAY_FUDGE; 1526 if (sm7.isDbg()) tlog("testStateMachine7: expect " + arrivalTimeDiff 1527 + " >= " + expectedDelay); 1528 assertTrue(arrivalTimeDiff >= expectedDelay); 1529 1530 if (sm7.isDbg()) tlog("testStateMachine7 X"); 1531 } 1532 1533 /** 1534 * Test unhandledMessage. 1535 */ 1536 class StateMachineUnhandledMessage extends StateMachine { 1537 StateMachineUnhandledMessage(String name) { 1538 super(name); 1539 mThisSm = this; 1540 setDbg(DBG); 1541 1542 // Setup state machine with 1 state 1543 addState(mS1); 1544 1545 // Set the initial state 1546 setInitialState(mS1); 1547 } 1548 @Override 1549 public void unhandledMessage(Message message) { 1550 mUnhandledMessageCount += 1; 1551 } 1552 1553 class S1 extends State { 1554 @Override 1555 public boolean processMessage(Message message) { 1556 if (message.what == TEST_CMD_2) { 1557 transitionToHaltingState(); 1558 } 1559 return NOT_HANDLED; 1560 } 1561 } 1562 1563 @Override 1564 protected void onHalting() { 1565 synchronized (mThisSm) { 1566 mThisSm.notifyAll(); 1567 } 1568 } 1569 1570 private StateMachineUnhandledMessage mThisSm; 1571 private int mUnhandledMessageCount; 1572 private S1 mS1 = new S1(); 1573 } 1574 1575 @SmallTest 1576 public void testStateMachineUnhandledMessage() throws Exception { 1577 1578 StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("smUnhandledMessage"); 1579 sm.start(); 1580 if (sm.isDbg()) tlog("testStateMachineUnhandledMessage E"); 1581 1582 synchronized (sm) { 1583 // Send 2 messages 1584 for (int i = 1; i <= 2; i++) { 1585 sm.sendMessage(i); 1586 } 1587 1588 try { 1589 // wait for the messages to be handled 1590 sm.wait(); 1591 } catch (InterruptedException e) { 1592 tloge("testStateMachineUnhandledMessage: exception while waiting " 1593 + e.getMessage()); 1594 } 1595 } 1596 1597 assertEquals(2, sm.getLogRecSize()); 1598 assertEquals(2, sm.mUnhandledMessageCount); 1599 1600 if (sm.isDbg()) tlog("testStateMachineUnhandledMessage X"); 1601 } 1602 1603 /** 1604 * Test state machines sharing the same thread/looper. Multiple instances 1605 * of the same state machine will be created. They will all share the 1606 * same thread and thus each can update <code>sharedCounter</code> which 1607 * will be used to notify testStateMachineSharedThread that the test is 1608 * complete. 1609 */ 1610 class StateMachineSharedThread extends StateMachine { 1611 StateMachineSharedThread(String name, Looper looper, int maxCount) { 1612 super(name, looper); 1613 mMaxCount = maxCount; 1614 setDbg(DBG); 1615 1616 // Setup state machine with 1 state 1617 addState(mS1); 1618 1619 // Set the initial state 1620 setInitialState(mS1); 1621 } 1622 1623 class S1 extends State { 1624 @Override 1625 public boolean processMessage(Message message) { 1626 if (message.what == TEST_CMD_4) { 1627 transitionToHaltingState(); 1628 } 1629 return HANDLED; 1630 } 1631 } 1632 1633 @Override 1634 protected void onHalting() { 1635 // Update the shared counter, which is OK since all state 1636 // machines are using the same thread. 1637 sharedCounter += 1; 1638 if (sharedCounter == mMaxCount) { 1639 synchronized (waitObject) { 1640 waitObject.notifyAll(); 1641 } 1642 } 1643 } 1644 1645 private int mMaxCount; 1646 private S1 mS1 = new S1(); 1647 } 1648 private static int sharedCounter = 0; 1649 private static Object waitObject = new Object(); 1650 1651 @MediumTest 1652 public void testStateMachineSharedThread() throws Exception { 1653 if (DBG) tlog("testStateMachineSharedThread E"); 1654 1655 // Create and start the handler thread 1656 HandlerThread smThread = new HandlerThread("testStateMachineSharedThread"); 1657 smThread.start(); 1658 1659 // Create the state machines 1660 StateMachineSharedThread sms[] = new StateMachineSharedThread[10]; 1661 for (int i = 0; i < sms.length; i++) { 1662 sms[i] = new StateMachineSharedThread("smSharedThread", 1663 smThread.getLooper(), sms.length); 1664 sms[i].start(); 1665 } 1666 1667 synchronized (waitObject) { 1668 // Send messages to each of the state machines 1669 for (StateMachineSharedThread sm : sms) { 1670 for (int i = 1; i <= 4; i++) { 1671 sm.sendMessage(i); 1672 } 1673 } 1674 1675 // Wait for the last state machine to notify its done 1676 try { 1677 waitObject.wait(); 1678 } catch (InterruptedException e) { 1679 tloge("testStateMachineSharedThread: exception while waiting " 1680 + e.getMessage()); 1681 } 1682 } 1683 1684 for (StateMachineSharedThread sm : sms) { 1685 assertEquals(4, sm.getLogRecCount()); 1686 for (int i = 0; i < sm.getLogRecSize(); i++) { 1687 LogRec lr = sm.getLogRec(i); 1688 assertEquals(i+1, lr.getWhat()); 1689 assertEquals(sm.mS1, lr.getState()); 1690 assertEquals(sm.mS1, lr.getOriginalState()); 1691 } 1692 } 1693 1694 if (DBG) tlog("testStateMachineSharedThread X"); 1695 } 1696 1697 static class Hsm1 extends StateMachine { 1698 private static final String HSM1_TAG = "hsm1"; 1699 1700 public static final int CMD_1 = 1; 1701 public static final int CMD_2 = 2; 1702 public static final int CMD_3 = 3; 1703 public static final int CMD_4 = 4; 1704 public static final int CMD_5 = 5; 1705 1706 public static Hsm1 makeHsm1() { 1707 Log.d(HSM1_TAG, "makeHsm1 E"); 1708 Hsm1 sm = new Hsm1(HSM1_TAG); 1709 sm.start(); 1710 Log.d(HSM1_TAG, "makeHsm1 X"); 1711 return sm; 1712 } 1713 1714 Hsm1(String name) { 1715 super(name); 1716 tlog("ctor E"); 1717 1718 // Add states, use indentation to show hierarchy 1719 addState(mP1); 1720 addState(mS1, mP1); 1721 addState(mS2, mP1); 1722 addState(mP2); 1723 1724 // Set the initial state 1725 setInitialState(mS1); 1726 tlog("ctor X"); 1727 } 1728 1729 class P1 extends State { 1730 @Override 1731 public void enter() { 1732 tlog("P1.enter"); 1733 } 1734 @Override 1735 public void exit() { 1736 tlog("P1.exit"); 1737 } 1738 @Override 1739 public boolean processMessage(Message message) { 1740 boolean retVal; 1741 tlog("P1.processMessage what=" + message.what); 1742 switch(message.what) { 1743 case CMD_2: 1744 // CMD_2 will arrive in mS2 before CMD_3 1745 sendMessage(CMD_3); 1746 deferMessage(message); 1747 transitionTo(mS2); 1748 retVal = true; 1749 break; 1750 default: 1751 // Any message we don't understand in this state invokes unhandledMessage 1752 retVal = false; 1753 break; 1754 } 1755 return retVal; 1756 } 1757 } 1758 1759 class S1 extends State { 1760 @Override 1761 public void enter() { 1762 tlog("S1.enter"); 1763 } 1764 @Override 1765 public void exit() { 1766 tlog("S1.exit"); 1767 } 1768 @Override 1769 public boolean processMessage(Message message) { 1770 tlog("S1.processMessage what=" + message.what); 1771 if (message.what == CMD_1) { 1772 // Transition to ourself to show that enter/exit is called 1773 transitionTo(mS1); 1774 return HANDLED; 1775 } else { 1776 // Let parent process all other messages 1777 return NOT_HANDLED; 1778 } 1779 } 1780 } 1781 1782 class S2 extends State { 1783 @Override 1784 public void enter() { 1785 tlog("S2.enter"); 1786 } 1787 @Override 1788 public void exit() { 1789 tlog("S2.exit"); 1790 } 1791 @Override 1792 public boolean processMessage(Message message) { 1793 boolean retVal; 1794 tlog("S2.processMessage what=" + message.what); 1795 switch(message.what) { 1796 case(CMD_2): 1797 sendMessage(CMD_4); 1798 retVal = true; 1799 break; 1800 case(CMD_3): 1801 deferMessage(message); 1802 transitionTo(mP2); 1803 retVal = true; 1804 break; 1805 default: 1806 retVal = false; 1807 break; 1808 } 1809 return retVal; 1810 } 1811 } 1812 1813 class P2 extends State { 1814 @Override 1815 public void enter() { 1816 tlog("P2.enter"); 1817 sendMessage(CMD_5); 1818 } 1819 @Override 1820 public void exit() { 1821 tlog("P2.exit"); 1822 } 1823 @Override 1824 public boolean processMessage(Message message) { 1825 tlog("P2.processMessage what=" + message.what); 1826 switch(message.what) { 1827 case(CMD_3): 1828 break; 1829 case(CMD_4): 1830 break; 1831 case(CMD_5): 1832 transitionToHaltingState(); 1833 break; 1834 } 1835 return HANDLED; 1836 } 1837 } 1838 1839 @Override 1840 protected void onHalting() { 1841 tlog("halting"); 1842 synchronized (this) { 1843 this.notifyAll(); 1844 } 1845 } 1846 1847 P1 mP1 = new P1(); 1848 S1 mS1 = new S1(); 1849 S2 mS2 = new S2(); 1850 P2 mP2 = new P2(); 1851 } 1852 1853 @MediumTest 1854 public void testHsm1() throws Exception { 1855 if (DBG) tlog("testHsm1 E"); 1856 1857 Hsm1 sm = Hsm1.makeHsm1(); 1858 1859 // Send messages 1860 sm.sendMessage(Hsm1.CMD_1); 1861 sm.sendMessage(Hsm1.CMD_2); 1862 1863 synchronized (sm) { 1864 // Wait for the last state machine to notify its done 1865 try { 1866 sm.wait(); 1867 } catch (InterruptedException e) { 1868 tloge("testHsm1: exception while waiting " + e.getMessage()); 1869 } 1870 } 1871 1872 dumpLogRecs(sm); 1873 1874 assertEquals(7, sm.getLogRecCount()); 1875 1876 LogRec lr = sm.getLogRec(0); 1877 assertEquals(Hsm1.CMD_1, lr.getWhat()); 1878 assertEquals(sm.mS1, lr.getState()); 1879 assertEquals(sm.mS1, lr.getOriginalState()); 1880 1881 lr = sm.getLogRec(1); 1882 assertEquals(Hsm1.CMD_2, lr.getWhat()); 1883 assertEquals(sm.mP1, lr.getState()); 1884 assertEquals(sm.mS1, lr.getOriginalState()); 1885 1886 lr = sm.getLogRec(2); 1887 assertEquals(Hsm1.CMD_2, lr.getWhat()); 1888 assertEquals(sm.mS2, lr.getState()); 1889 assertEquals(sm.mS2, lr.getOriginalState()); 1890 1891 lr = sm.getLogRec(3); 1892 assertEquals(Hsm1.CMD_3, lr.getWhat()); 1893 assertEquals(sm.mS2, lr.getState()); 1894 assertEquals(sm.mS2, lr.getOriginalState()); 1895 1896 lr = sm.getLogRec(4); 1897 assertEquals(Hsm1.CMD_3, lr.getWhat()); 1898 assertEquals(sm.mP2, lr.getState()); 1899 assertEquals(sm.mP2, lr.getOriginalState()); 1900 1901 lr = sm.getLogRec(5); 1902 assertEquals(Hsm1.CMD_4, lr.getWhat()); 1903 assertEquals(sm.mP2, lr.getState()); 1904 assertEquals(sm.mP2, lr.getOriginalState()); 1905 1906 lr = sm.getLogRec(6); 1907 assertEquals(Hsm1.CMD_5, lr.getWhat()); 1908 assertEquals(sm.mP2, lr.getState()); 1909 assertEquals(sm.mP2, lr.getOriginalState()); 1910 1911 if (DBG) tlog("testStateMachineSharedThread X"); 1912 } 1913 1914 private static void tlog(String s) { 1915 Log.d(TAG, s); 1916 } 1917 1918 private static void tloge(String s) { 1919 Log.e(TAG, s); 1920 } 1921 } 1922