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