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