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