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