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