1 /* 2 * Copyright (C) 2008 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.cts; 18 19 import com.android.cts.TestHost.ActionType; 20 import com.android.cts.TestHost.MODE; 21 22 import org.xml.sax.SAXException; 23 24 import java.io.BufferedReader; 25 import java.io.File; 26 import java.io.FileNotFoundException; 27 import java.io.IOException; 28 import java.io.InputStreamReader; 29 import java.security.NoSuchAlgorithmException; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.HashMap; 33 import java.util.List; 34 35 import javax.xml.parsers.ParserConfigurationException; 36 import javax.xml.transform.TransformerException; 37 import javax.xml.transform.TransformerFactoryConfigurationError; 38 39 /** 40 * Main console of CTS providing user with the interface to interact. <BR> 41 * Using CommandParser to parse command line argument and process. 42 * <ul> 43 * <li> start a test plan 44 * <li> remove a test plan 45 * <li> add a test package 46 * <li> remove a test package 47 * <li> list current available devices 48 * <li> list current available test plan 49 * <li> list current available package 50 * <li> list current test result 51 * <li> view CTS' status, uninitialized, idle or running 52 * <li> view command history 53 * <li> select a history command to run 54 * </ul> 55 */ 56 public class ConsoleUi { 57 58 private static final String OS_NAME_LINUX = "Linux"; 59 private static final String LS_PLAN_SEPARATOR = "================================="; 60 private static final String CMD_TYPE_LEADING_SPACE = " "; 61 private static final String CMD_OPT_LEADING_SPACE = " "; 62 private static final String CREATE_SESSION = "create a new session"; 63 private static final String CHOOSE_SESSION = "choose a session"; 64 65 private TestHost mHost; 66 private boolean mKeepRunning; 67 private BufferedReader mCommandInput; 68 // private static ConsoleInputStream sConsoleReader; 69 private CommandHistory mCommandHistory = new CommandHistory(); 70 private String mOsName = "none"; 71 72 // Define test case name pattern 73 static final String CASE_NAME_PATTERN_STR = "((\\S+\\.)+\\S+)\\.(\\S+):(\\S+)"; 74 private static HashMap<String, Integer> mResultCodeMap; 75 76 static { 77 mResultCodeMap = new HashMap<String, Integer>(); 78 mResultCodeMap.put(CtsTestResult.STR_PASS, CtsTestResult.CODE_PASS); 79 mResultCodeMap.put(CtsTestResult.STR_FAIL, CtsTestResult.CODE_FAIL); 80 mResultCodeMap.put(CtsTestResult.STR_ERROR, CtsTestResult.CODE_ERROR); 81 mResultCodeMap.put(CtsTestResult.STR_NOT_EXECUTED, CtsTestResult.CODE_NOT_EXECUTED); 82 mResultCodeMap.put(CtsTestResult.STR_TIMEOUT, CtsTestResult.CODE_TIMEOUT); 83 } 84 85 public ConsoleUi(TestHost host) { 86 mHost = host; 87 mCommandInput = new BufferedReader(new InputStreamReader(System.in)); 88 mKeepRunning = true; 89 90 initInputStream(); 91 } 92 93 /** 94 * Start the console user interface. 95 * 96 */ 97 public void startUi() { 98 while (mKeepRunning) { 99 try { 100 String cmdLine = readLine(CUIOutputStream.CTS_PROMPT_SIGN); 101 CommandParser cp = CommandParser.parse(cmdLine); 102 processCommand(cp); 103 mCommandHistory.addCommand(cp, cmdLine); 104 } catch (CommandNotFoundException e) { 105 // avoid displaying help message for empty 106 // command by pressing ENTER over console directly 107 } catch (Exception e) { 108 Log.e("Got exception while processing command.", e); 109 showHelp(); 110 } 111 } 112 } 113 114 /** 115 * Initialize the CommandProcessor. 116 */ 117 private void initInputStream() { 118 // mOsName = System.getProperty("os.name"); 119 if (mOsName.equals(OS_NAME_LINUX)) { 120 // sConsoleReader = new ConsoleInputStream(new FileInputStream( 121 // FileDescriptor.in), mCommandHistory); 122 // sConsoleReader.setup(); 123 } else { 124 mCommandInput = new BufferedReader(new InputStreamReader(System.in)); 125 } 126 } 127 128 /** 129 * Read a message line from console. 130 * 131 * @param prompt The notification message print out to console before reading. 132 * @return The string user typed in. 133 */ 134 private String readLine(String prompt) throws IOException { 135 String cmdLine = null; 136 if (mOsName.equals(OS_NAME_LINUX)) { 137 // cmdLine = sConsoleReader.readLine(prompt).trim(); 138 } else { 139 CUIOutputStream.print(prompt); 140 cmdLine = mCommandInput.readLine().trim(); 141 } 142 return cmdLine; 143 } 144 145 /** 146 * Display the help message. 147 */ 148 private void showHelp() { 149 CUIOutputStream.println("Usage: command options"); 150 CUIOutputStream.println("Avaiable commands and options:"); 151 showHostCmdHelp(); 152 showPlanCmdHelp(); 153 showPackageCmdHelp(); 154 showResultCmdHelp(); 155 showHistoryCmdHelp(); 156 showDeviceCmdHelp(); 157 } 158 159 /** 160 * Display the help message related to history commands. 161 */ 162 private void showHistoryCmdHelp() { 163 final String cmdStr = CTSCommand.HISTORY + "/" + CTSCommand.H; 164 165 CUIOutputStream.println(CMD_TYPE_LEADING_SPACE + "History:"); 166 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 167 + cmdStr + ": list all commands in command history"); 168 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 169 + cmdStr + " count: list the latest count records in command history"); 170 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 171 + cmdStr + " " + CTSCommand.OPTION_E 172 + " num: run the command designated by 'num' in command history"); 173 } 174 175 /** 176 * Display the help message related to result commands. 177 */ 178 private void showResultCmdHelp() { 179 final String cmdStr = CTSCommand.LIST + " " + CTSCommand.OPTION_R 180 + "/" + CTSCommand.OPTION_RESULT; 181 final String sessionStr = CTSCommand.OPTION_S + "/" + CTSCommand.OPTION_SESSION; 182 final String resultsStr = " [" + CtsTestResult.STR_PASS 183 + "/" + CtsTestResult.STR_FAIL 184 + "/" + CtsTestResult.STR_NOT_EXECUTED 185 + "/" + CtsTestResult.STR_TIMEOUT 186 + "] "; 187 188 CUIOutputStream.println(CMD_TYPE_LEADING_SPACE + "Result:"); 189 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 190 + cmdStr + ": list all result of sessions"); 191 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 192 + cmdStr + " " + sessionStr 193 + " session_id: list detail case result of a specified session"); 194 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 195 + cmdStr + resultsStr + sessionStr 196 + " session_id: list detail cases of a specified" 197 + " session by the specified result."); 198 } 199 200 /** 201 * Display the help message related to package commands. 202 */ 203 private void showPackageCmdHelp() { 204 final String cmdStr = CTSCommand.LIST + " " + CTSCommand.OPTION_P 205 + "/" + CTSCommand.OPTION_PACKAGE; 206 final String pkgStr = CTSCommand.OPTION_P + "/" + CTSCommand.OPTION_PACKAGE; 207 208 CUIOutputStream.println(CMD_TYPE_LEADING_SPACE + "Package:"); 209 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 210 + cmdStr + ": list available packages"); 211 CUIOutputStream.println(CMD_OPT_LEADING_SPACE + cmdStr + " package_name: " 212 + "list contents of the package with specified name"); 213 214 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 215 + CTSCommand.ADD + " " + pkgStr 216 + " root: add packages from root to repository"); 217 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 218 + CTSCommand.REMOVE + " " + pkgStr + " package_name/all: " 219 + "remove a package or all packages from repository"); 220 } 221 222 /** 223 * Display the help message related to plan commands. 224 */ 225 private void showPlanCmdHelp() { 226 final String lsPlanStr = CTSCommand.LIST + " " + CTSCommand.OPTION_PLAN; 227 final String addPlanStr = CTSCommand.ADD + " " + CTSCommand.OPTION_PLAN; 228 final String rmPlanStr = CTSCommand.REMOVE + " " + CTSCommand.OPTION_PLAN; 229 final String addDerivedPlanStr = CTSCommand.ADD + " " + CTSCommand.OPTION_DERIVED_PLAN; 230 231 CUIOutputStream.println(CMD_TYPE_LEADING_SPACE + "Plan:"); 232 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 233 + lsPlanStr + ": list available plans"); 234 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 235 + lsPlanStr + " plan_name: list contents of the plan with specified name"); 236 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 237 + addPlanStr + " plan_name: add a new plan with specified name"); 238 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 239 + addDerivedPlanStr + " plan_name " 240 + CTSCommand.OPTION_S + "/" + CTSCommand.OPTION_SESSION + " session_id " 241 + CTSCommand.OPTION_R + "/" + CTSCommand.OPTION_RESULT + " result_type" 242 + ": derive a plan from the given session"); 243 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 244 + rmPlanStr + " plan_name/all: remove a plan or all plans from repository"); 245 showStartSessionHelp(); 246 } 247 248 /** 249 * Display the help message related to start session command. 250 */ 251 private void showStartSessionHelp() { 252 final String cmdStr = CTSCommand.START + " " + CTSCommand.OPTION_PLAN; 253 final String testStr = CTSCommand.OPTION_T + "/" + CTSCommand.OPTION_TEST; 254 final String deviceStr = CTSCommand.OPTION_D + "/" + CTSCommand.OPTION_DEVICE; 255 final String pkgStr = CTSCommand.OPTION_P + "/" + CTSCommand.OPTION_PACKAGE; 256 257 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 258 + cmdStr + " test_plan_name: run a test plan"); 259 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 260 + cmdStr + " test_plan_name " + deviceStr + " device_ID" 261 + ": run a test plan using the specified device"); 262 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 263 + cmdStr + " test_plan_name " + testStr + " test_name" 264 + ": run a specific test"); 265 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 266 + cmdStr + " test_plan_name " + pkgStr + " java_package_name" 267 + ": run a specific java package"); 268 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 269 + cmdStr + " test_plan_name " + testStr + " test_name " 270 + deviceStr + " device_ID" 271 + ": run a specific test using the specified device"); 272 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 273 + cmdStr + " test_plan_name " + pkgStr + " java_package_name " 274 + deviceStr + " device_ID" 275 + ": run a specific java package using the specified device"); 276 } 277 278 /** 279 * Display the help message related to host commands. 280 */ 281 private void showHostCmdHelp() { 282 CUIOutputStream.println(CMD_TYPE_LEADING_SPACE + "Host:"); 283 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 284 + CTSCommand.HELP + ": show this message"); 285 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 286 + CTSCommand.EXIT + ": exit cts command line"); 287 } 288 289 /** 290 * Display the help message related to device commands. 291 */ 292 private void showDeviceCmdHelp() { 293 final String deviceStr = CTSCommand.OPTION_D + "/" + CTSCommand.OPTION_DEVICE; 294 295 CUIOutputStream.println(CMD_TYPE_LEADING_SPACE + "Device:"); 296 CUIOutputStream.println(CMD_OPT_LEADING_SPACE 297 + CTSCommand.LIST + " " + deviceStr + ": list available devices"); 298 } 299 300 /** 301 * Process the command from user's input. 302 * 303 * @param cp Command container. 304 */ 305 public void processCommand(final CommandParser cp) throws Exception { 306 String action = cp.getAction(); 307 308 if (action.equals(CTSCommand.EXIT)) { 309 if (cp.getArgSize() != 1) { 310 showHelp(); 311 return; 312 } 313 Log.d("exit cts host"); 314 mKeepRunning = false; 315 mHost.tearDown(); 316 } else if (action.equals(CTSCommand.HELP)) { 317 showHelp(); 318 } else if (mCommandHistory.isHistoryCommand(action)) { 319 processHistoryCommands(cp); 320 } else if (action.equals(CTSCommand.ADD)) { 321 processAddCommand(cp); 322 } else if (action.equals(CTSCommand.START)) { 323 processStartCommand(cp); 324 } else if (action.equals(CTSCommand.REMOVE)) { 325 processRmCommand(cp); 326 } else if (action.equals(CTSCommand.LIST)) { 327 processListCommand(cp); 328 } else { 329 showHelp(); 330 } 331 } 332 333 /** 334 * Process start command. 335 * 336 * @param cp Command container. 337 */ 338 private void processStartCommand(CommandParser cp) throws SAXException, 339 ParserConfigurationException { 340 if (cp.containsKey(CTSCommand.OPTION_PLAN)) { 341 processStartSessionCommand(cp); 342 } else if (cp.containsKey(CTSCommand.OPTION_P) 343 || cp.containsKey(CTSCommand.OPTION_PACKAGE)) { 344 processStartPackageCommand(cp); 345 } else { 346 showHelp(); 347 } 348 } 349 350 /** 351 * Process start package command. 352 * 353 * <ul> 354 * <li> Syntax: 355 * start --package zipped-package-file 356 * </ul> 357 * @param cp Command container. 358 */ 359 private void processStartPackageCommand(CommandParser cp) { 360 try { 361 String pathName = cp.getValue(CTSCommand.OPTION_PACKAGE); 362 mHost.startZippedPackage(pathName); 363 } catch (DeviceDisconnectedException e) { 364 Log.e("Device " + e.getMessage() + " disconnected", e); 365 } catch (Exception e) { 366 Log.e("Met exception during running zipped package.", e); 367 } 368 } 369 370 /** 371 * Validate the command parameters used to activate CTS. 372 * 373 * @param cp Command container. 374 * @return If command parameters are valid, return true; else, return false. 375 */ 376 public boolean validateCommandParams(CommandParser cp) { 377 if (cp == null) { 378 return false; 379 } 380 381 if (cp.getAction() == null) { 382 return true; 383 } else if (isValidCommandOption(cp, CTSCommand.START, 384 CTSCommand.OPTION_PLAN)) { 385 return true; 386 } else if (isValidCommandOption(cp, CTSCommand.START, 387 CTSCommand.OPTION_PACKAGE)) { 388 return true; 389 } else if (isValidCommandOption(cp, CTSCommand.START, 390 CTSCommand.OPTION_P)) { 391 return true; 392 } else { 393 return false; 394 } 395 } 396 397 /** 398 * Check if the command option is valid. 399 * 400 * @param cp CommandParser which contains the command and options. 401 * @param command Command the user typed in. 402 * @param option Option the user typed in. 403 * @return If command option valid, return true; else, return false. 404 */ 405 private static boolean isValidCommandOption(CommandParser cp, 406 String command, String option) { 407 return (cp.getAction().equals(command)) && (cp.containsKey(option)) 408 && (cp.getValue(option) != null) 409 && (cp.getValue(option).length() != 0); 410 } 411 412 /** 413 * Process start session command. 414 * <ul> 415 * <li> Syntax 1: 416 * start --plan plan-name 417 * [ --device device-id ] 418 * [ --test test-name ] 419 * [ --profile profile-name ] 420 * <li> Syntax 2: 421 * start --plan plan-name 422 * [ --device device-id ] 423 * [ --package java-package-name ] 424 * [ --profile profile-name ] 425 * </ul> 426 * @param cp container which contained start command options and values 427 * Process the list commands. 428 */ 429 private void processStartSessionCommand(CommandParser cp) 430 throws SAXException, ParserConfigurationException { 431 432 if (mHost.getDeviceList().length == 0) { 433 Log.e("No device connected", null); 434 return; 435 } 436 437 String testPlanPath = null; 438 String deviceId = null; 439 String testName = null; 440 String javaPkgName = null; 441 String testPlanName = mHost.getPlanName(cp.getValue(CTSCommand.OPTION_PLAN)); 442 try { 443 if (cp.getActionValues().size() != 0 || cp.getOptionSize() < 1 444 || cp.getOptionSize() > 3) { 445 showStartSessionHelp(); 446 return; 447 } 448 testPlanName = mHost.getPlanName(cp 449 .getValue(CTSCommand.OPTION_PLAN)); 450 testPlanPath = HostConfig.getInstance().getPlanRepository() 451 .getPlanPath(testPlanName); 452 if (testPlanPath == null) { 453 CUIOutputStream.println("Plan " + testPlanName 454 + " is not in repository, please create it!"); 455 return; 456 } 457 458 ActionType actionType = ActionType.START_NEW_SESSION; 459 if (cp.containsKey(CTSCommand.OPTION_TEST)) { 460 testName = cp.getValue(CTSCommand.OPTION_TEST); 461 if (-1 == testName.indexOf(Test.METHOD_SEPARATOR)) { 462 Log.e("Test full name must be in the form of:" 463 + " java_package_name.class_name#method_name.", null); 464 return; 465 } 466 actionType = ActionType.RUN_SINGLE_TEST; 467 } else if (cp.containsKey(CTSCommand.OPTION_PACKAGE)) { 468 javaPkgName = cp.getValue(CTSCommand.OPTION_PACKAGE); 469 actionType = ActionType.RUN_SINGLE_JAVA_PACKAGE; 470 } 471 472 TestSession ts = null; 473 ArrayList<TestSession> sessionList = mHost.getSessionList(testPlanName); 474 if ((sessionList != null) && (sessionList.size() > 0)) { 475 if ((testName == null) || (testName.length() == 0)) { 476 String mode = chooseMode(sessionList); 477 if (CREATE_SESSION.equals(mode)) { 478 ts = TestHost.createSession(testPlanName); 479 } 480 } 481 if (ts == null) { 482 ts = chooseTestSession(sessionList); 483 deviceId = ts.getDeviceId(); 484 if ((actionType != ActionType.RUN_SINGLE_TEST) 485 && (actionType != ActionType.RUN_SINGLE_JAVA_PACKAGE)) { 486 actionType = ActionType.RESUME_SESSION; 487 } 488 } 489 } 490 491 if (cp.containsKey(CTSCommand.OPTION_DEVICE)) { 492 deviceId = cp.getValue(CTSCommand.OPTION_DEVICE); 493 String[] deviceIdList = deviceId.trim().split(","); 494 if (deviceIdList.length > 1) { 495 Log.e("Just allow choosing one device ID.", null); 496 return; 497 } 498 } 499 500 if (deviceId == null) { 501 TestDevice td = mHost.getFirstAvailableDevice(); 502 if (td == null) { 503 // no devices attached 504 CUIOutputStream.println("No idle devices found."); 505 return; 506 } 507 deviceId = td.getSerialNumber(); 508 } 509 510 if (!checkDeviceExists(mHost.getDeviceList(), deviceId)) { 511 CUIOutputStream.println("Can't find specified device id. Is it attached?"); 512 return; 513 } 514 515 if (ts == null) { 516 ts = TestHost.createSession(testPlanName); 517 } 518 519 mHost.startSession(ts, deviceId, testName, javaPkgName, actionType); 520 } catch (IOException e) { 521 Log.e("Can't create test session", e); 522 } catch (DeviceNotAvailableException e) { 523 CUIOutputStream.println("Test plan(" + testPlanName + ") " 524 + e.getMessage()); 525 showStartSessionHelp(); 526 } catch (TestNotFoundException e) { 527 CUIOutputStream.println(e.getMessage()); 528 } catch (TestPlanNotFoundException e) { 529 CUIOutputStream.println("Can't find test plan " + testPlanName); 530 } catch (IllegalTestNameException e) { 531 CUIOutputStream.println("Illegal case name: " + testName); 532 } catch (DeviceDisconnectedException e) { 533 Log.e("Device " + e.getMessage() + " disconnected ", null); 534 } catch (NoSuchAlgorithmException e) { 535 Log.e("Fail to initialise SHA-1 algorithm", e); 536 } catch (InvalidApkPathException e) { 537 Log.e(e.getMessage(), null); 538 } catch (InvalidNameSpaceException e) { 539 Log.e(e.getMessage(), null); 540 } 541 } 542 543 /** 544 * Choose test session among the available test session list. 545 * 546 * @param sessionList The available test session list. 547 * @return The test session chosen. 548 */ 549 private TestSession chooseTestSession(ArrayList<TestSession> sessionList) throws IOException { 550 if ((sessionList == null) || (sessionList.size() == 0)) { 551 return null; 552 } 553 554 if (sessionList.size() == 1) { 555 return sessionList.get(0); 556 } 557 558 int index = 0; 559 String notification = "Please choose a session from the existed session(s):\n"; 560 for (TestSession session : sessionList) { 561 notification += " " + session.getId() + " [" + index + "] \n"; 562 index ++; 563 } 564 565 return sessionList.get(getUserInputId(notification, 0, index)); 566 } 567 568 /** 569 * Choose between creating a new session and choosing a session among available ones. 570 * 571 * @param sessionList The available test session list. 572 * @return If choose to create a new session, return CREATE_SESSION; 573 * else return CHOOSE_SESSION. 574 */ 575 private String chooseMode(ArrayList<TestSession> sessionList) throws IOException { 576 if (TestHost.sMode == MODE.RUN || (sessionList == null) || (sessionList.size() == 0)) { 577 // do not prompt if the test run was started from command line mode, or when 578 // there are no existing sessions 579 return CREATE_SESSION; 580 } 581 582 String planName = sessionList.get(0).getSessionLog().getTestPlanName(); 583 String notification = "There are " + sessionList.size() 584 + " existing session(s) for plan " + planName + ".\n" 585 + "Create a new session or choose an existing one?\n" 586 + " Create a new session [0]\n" 587 + " Choose a session [1]\n"; 588 589 int indexSelected = getUserInputId(notification, 0, 2); 590 if (indexSelected == 0) { 591 return CREATE_SESSION; 592 } else { 593 return CHOOSE_SESSION; 594 } 595 } 596 597 /** 598 * Validate the specified device ID against the available device array. 599 * 600 * @param availableDevices The available device array. 601 * @param specifiedId The specified device ID list. 602 * @return true if the id is valid 603 */ 604 public boolean checkDeviceExists(TestDevice[] availableDevices, String specifiedId) { 605 for (TestDevice dev : availableDevices) { 606 if (specifiedId.equals(dev.getSerialNumber())) { 607 return true; 608 } 609 } 610 return false; 611 } 612 613 /** 614 * Get device ID from the device ID string against the available devices. 615 * 616 * @param availableDevices The available devices. 617 * @param idStr The device ID string. 618 * @return The device ID. 619 */ 620 public int getDeviceId(TestDevice[] availableDevices, String idStr) { 621 for (int i = 0; i < availableDevices.length; i++) { 622 TestDevice dev = availableDevices[i]; 623 if (idStr.equals(dev.getSerialNumber())) { 624 return i; 625 } 626 } 627 return -1; 628 } 629 630 /** 631 * Get the ID input by the against the specified range. 632 * 633 * @param notification The notification message to notify the user. 634 * @param startIndex The start index. 635 * @param endIndex The end index. 636 * @return The selected index of the ID the user chosen. 637 */ 638 private int getUserInputId(String notification, int startIndex, int endIndex) 639 throws IOException { 640 int indexSelected = 0; 641 boolean success = false; 642 while (!success) { 643 String answer = readLine(notification); 644 try { 645 indexSelected = Integer.parseInt(answer); 646 if ((indexSelected >= 0) && (indexSelected < endIndex)) { 647 success = true; 648 } else { 649 CUIOutputStream.println("" + indexSelected 650 + " is out of range [0," + (endIndex -1 ) + "]."); 651 } 652 } catch (NumberFormatException e) { 653 CUIOutputStream.println("Invalid nuber is typed in."); 654 } 655 } 656 return indexSelected; 657 } 658 659 /** 660 * Check if the specified device ID is valid. 661 * 662 * @param numOfAvailableDevices The number of available devices. 663 * @param specifiedId The specified device ID. 664 * @return If the specified ID contained in available ID list, 665 * return true; else, return false. 666 */ 667 public boolean isValidDeviceId(int numOfAvailableDevices, int specifiedId) { 668 if (specifiedId < 0 || specifiedId >= numOfAvailableDevices) { 669 return false; 670 } 671 return true; 672 } 673 674 /** 675 * Process list commands. 676 * <ul> 677 * <li> Syntax 1: 678 * ls --device 679 * <li> Syntax 2: 680 * ls --plan [ plan-name ] 681 * <li> Syntax 3: 682 * ls --package [ package-name ] 683 * <li> Syntax 4: 684 * ls --result 685 * [ pass/fail/notExecuted/timeout ] 686 * [ --session session_id ] 687 * </ul> 688 * 689 * @param cp Command container. 690 */ 691 private void processListCommand(CommandParser cp) throws SAXException, 692 IOException, ParserConfigurationException { 693 if (cp.containsKey(CTSCommand.OPTION_DEVICE)) { 694 if (cp.getActionValues().size() != 0 || cp.getOptionSize() != 1) { 695 showDeviceCmdHelp(); 696 return; 697 } 698 if (cp.getValue(CTSCommand.OPTION_DEVICE).equals("")) { 699 listDevices(); 700 } else { 701 showDeviceCmdHelp(); 702 } 703 } else if (cp.containsKey(CTSCommand.OPTION_PLAN)) { 704 if (cp.getActionValues().size() != 0 || cp.getOptionSize() != 1) { 705 showPlanCmdHelp(); 706 return; 707 } 708 String planValue = cp.getValue(CTSCommand.OPTION_PLAN); 709 if (planValue.equals("")) { 710 listPlans(); 711 } else { 712 listSinglePlan(mHost.getPlanName(planValue)); 713 } 714 } else if (cp.containsKey(CTSCommand.OPTION_RESULT)) { 715 if (cp.getActionValues().size() != 0 716 || (cp.getOptionSize() < 1 || cp.getOptionSize() > 2)) { 717 showResultCmdHelp(); 718 return; 719 } 720 String resultValue = cp.getValue(CTSCommand.OPTION_RESULT); 721 String sessionId = cp.getValue(CTSCommand.OPTION_SESSION); 722 Integer resultCode = null; 723 724 if (sessionId != null) { 725 if (resultValue.length() != 0 726 && !mResultCodeMap.containsKey(resultValue)) { 727 showResultCmdHelp(); 728 } else { 729 resultCode = mResultCodeMap.get(resultValue); 730 listSessionResult(sessionId, resultCode); 731 } 732 } else if (resultValue.length() == 0) { 733 listResults(); 734 } else { 735 showHelp(); 736 } 737 } else if (cp.containsKey(CTSCommand.OPTION_PACKAGE)) { 738 if (cp.getActionValues().size() != 0 || cp.getOptionSize() != 1) { 739 showPackageCmdHelp(); 740 return; 741 } 742 listPackages(cp); 743 } else { 744 showHelp(); 745 } 746 } 747 748 /** 749 * Process the removing commands. 750 * <ul> 751 * <li> Syntax 1: 752 * rm --plan [ plan-name ] [ all ] 753 * <li> Syntax 2: 754 * rm --package [ package-name ] [ all ] 755 * </ul> 756 * 757 * @param cp Command container. 758 */ 759 private void processRmCommand(CommandParser cp) throws IOException { 760 if (cp.containsKey(CTSCommand.OPTION_PLAN)) { 761 if (cp.getActionValues().size() != 0 || cp.getOptionSize() != 1) { 762 showPlanCmdHelp(); 763 return; 764 } 765 766 String planName = mHost.getPlanName(cp.getValue(CTSCommand.OPTION_PLAN)); 767 if (HostConfig.ALL.equals(planName)) { 768 String prompt = "Remove all of the plans?([y/N])"; 769 String answer = readLine(prompt).trim(); 770 if (!isConfirmation(answer, false)) { 771 return; 772 } 773 } 774 775 mHost.removePlans(planName); 776 } else if (cp.containsKey(CTSCommand.OPTION_PACKAGE)) { 777 if (cp.getActionValues().size() != 0 || cp.getOptionSize() != 1) { 778 showPackageCmdHelp(); 779 return; 780 } 781 782 String packageName = cp.getValue(CTSCommand.OPTION_PACKAGE); 783 if (HostConfig.ALL.equals(packageName)) { 784 String prompt = "Remove all of the packages?([y/N])"; 785 String answer = readLine(prompt).trim(); 786 if (!isConfirmation(answer, false)) { 787 return; 788 } 789 } 790 791 mHost.removePackages(packageName); 792 } else { 793 showHelp(); 794 } 795 } 796 797 /** 798 * Check if the answer is confirmation. 799 * 800 * @param answer The answer user typed in. 801 * @param defaultResult If true, default to yes; else, default to no. 802 * @return If confirmation, return true; else, return false. 803 */ 804 public static boolean isConfirmation(String answer, boolean defaultResult) { 805 if ("".equals(answer)) { 806 return defaultResult; 807 } 808 809 return ("y".equals(answer.toLowerCase()) || "yes".equals(answer.toLowerCase())); 810 } 811 812 /** 813 * Process the add commands. 814 * <ul> 815 * <li> Syntax 1: 816 * add --plan plan-name 817 * <li> Syntax 2: 818 * add --package package-name 819 * </ul> 820 * 821 * @param cp Command container. 822 */ 823 private void processAddCommand(CommandParser cp) { 824 if (cp.containsKey(CTSCommand.OPTION_PLAN)) { 825 if (isValidAddPlanArguments(cp)) { 826 createPlan(cp, CTSCommand.OPTION_PLAN); 827 } else { 828 showPlanCmdHelp(); 829 } 830 } else if (cp.containsKey(CTSCommand.OPTION_DERIVED_PLAN)) { 831 if (isValidDerivedPlanArguments(cp)) { 832 createPlan(cp, CTSCommand.OPTION_DERIVED_PLAN); 833 } else { 834 showPlanCmdHelp(); 835 } 836 } else if (cp.containsKey(CTSCommand.OPTION_PACKAGE)) { 837 try { 838 addPackage(cp); 839 } catch (IOException e) { 840 Log.e("Can't add package", e); 841 } catch (IndexOutOfBoundsException e) { 842 Log.e("Can't add package", e); 843 } catch (NoSuchAlgorithmException e) { 844 Log.e("Can't add package", e); 845 } 846 } else { 847 showHelp(); 848 } 849 } 850 851 /** 852 * Check if it's valid arguments for adding plan. 853 * 854 * @param cp The command processor. 855 * @return if valid, return true; else, return false. 856 */ 857 private boolean isValidAddPlanArguments(CommandParser cp) { 858 return (cp.getArgSize() == 3) && (cp.getActionValues().size() == 0) 859 && (cp.getOptionSize() == 1); 860 } 861 862 /** 863 * Check if it's valid arguments for deriving plan. 864 * 865 * @param cp The command processor. 866 * @return if valid, return true; else, return false. 867 */ 868 private boolean isValidDerivedPlanArguments(CommandParser cp) { 869 //argument size: it's at least 3, as "add --plan plan_name" 870 //action values: no option contains more than one value 871 //option size: it's at least 1, as "add --plan plan_name" 872 return (cp.getArgSize() >= 3) && (cp.getActionValues().size() == 0) 873 && (cp.getOptionSize() >= 1); 874 } 875 876 /** 877 * Process the history commands. 878 * <ul> 879 * <li> Syntax: 880 * history [ -e ] [ number] 881 * </ul> 882 * 883 * @param cp Command container. 884 */ 885 private void processHistoryCommands(final CommandParser cp) 886 throws Exception { 887 try { 888 if ((cp.getOptionSize() == 0) && (cp.getActionValues().size() == 0)) { 889 mCommandHistory.show(mCommandHistory.size()); 890 } else if (cp.containsKey(CTSCommand.OPTION_E) 891 && (cp.getActionValues().size() == 0)) { 892 int cmdNum = 0; 893 cmdNum = Integer.parseInt(cp.getValue(CTSCommand.OPTION_E)); 894 if (cmdNum >= 0 && cmdNum < mCommandHistory.size()) { 895 String cmdLine = mCommandHistory.get(cmdNum); 896 CommandParser cpH = CommandParser.parse(cmdLine); 897 CUIOutputStream.printPrompt(); 898 CUIOutputStream.println(cmdLine);// print(CTS_PROMPT_SIGN 899 // + cmdLine); 900 processCommand(cpH); 901 mCommandHistory.addCommand(cpH, cmdLine); 902 } else { 903 if (mCommandHistory.size() > 0) { 904 Log.e("Command index " + cmdNum 905 + " is out of command history range [0," 906 + (mCommandHistory.size() - 1) + "].", null); 907 } else { 908 Log.e("No command exists in command history.", null); 909 } 910 } 911 } else if ((cp.getOptionSize() == 0) 912 && (cp.getActionValues().size() == 1)) { 913 int cmdCount = Integer.parseInt(cp.getActionValues().iterator() 914 .next()); 915 if (cmdCount < 0 || cmdCount > mCommandHistory.size()) { 916 cmdCount = mCommandHistory.size(); 917 } 918 mCommandHistory.show(cmdCount); 919 } else { 920 showHistoryCmdHelp(); 921 } 922 923 } catch (NumberFormatException e) { 924 showHistoryCmdHelp(); 925 } 926 } 927 928 /** 929 * List a single plan by the plan name given. 930 * 931 * @param name The plan name. 932 */ 933 private void listSinglePlan(String name) throws SAXException, IOException, 934 ParserConfigurationException { 935 String planName = null; 936 for (String str : mHost.getPlanRepository().getAllPlanNames()) { 937 if (str.startsWith(name)) { 938 planName = str; 939 break; 940 } 941 } 942 943 if (planName == null) { 944 Log.e("No plan named " + name + " in repository!", null); 945 return; 946 } 947 948 String planPath = mHost.getPlanRepository().getPlanPath(planName); 949 ArrayList<String> removedPkgList = new ArrayList<String>(); 950 Collection<String> pkgNames = TestPlan.getEntries(planPath, removedPkgList); 951 952 if (removedPkgList.size() != 0) { 953 CUIOutputStream.println("The following package(s) contained in plan " 954 + planName + " have been removed:"); 955 for (String pkgName : removedPkgList) { 956 CUIOutputStream.println(" " + pkgName); 957 } 958 } 959 960 if (pkgNames.size() > 0) { 961 CUIOutputStream.println("Packages of plan " + planName 962 + " (" + pkgNames.size() + " in total):"); 963 CUIOutputStream.println(LS_PLAN_SEPARATOR); 964 for (String pkgName : pkgNames) { 965 CUIOutputStream.println(pkgName); 966 } 967 } 968 } 969 970 /** 971 * Create test plan via the test session and result type given. 972 * 973 * @param name The test plan name. 974 * @param ts The test session. 975 * @param resultType The result type. 976 */ 977 private void createPlanFromSession(final String name, TestSession ts, final String resultType) 978 throws FileNotFoundException, IOException, ParserConfigurationException, 979 TransformerFactoryConfigurationError, TransformerException { 980 981 HashMap<String, ArrayList<String>> selectedResult = 982 new HashMap<String, ArrayList<String>>(); 983 ArrayList<String> packageNames = new ArrayList<String>(); 984 985 for (TestPackage pkg : ts.getSessionLog().getTestPackages()) { 986 String pkgName = pkg.getAppPackageName(); 987 ArrayList<String> excludedList = pkg.getExcludedList(resultType); 988 if (excludedList != null) { 989 packageNames.add(pkgName); 990 selectedResult.put(pkgName, excludedList); 991 } 992 } 993 994 if ((selectedResult != null) && (selectedResult.size() > 0)) { 995 TestSessionBuilder.getInstance().serialize(name, packageNames, selectedResult); 996 } else { 997 if (resultType == null) { 998 Log.i("All tests of session " + ts.getId() 999 + " have passed execution. The plan is not created!"); 1000 } else { 1001 Log.i("No " + resultType + " tests of session " + ts.getId() 1002 + ". The plan is not created!"); 1003 } 1004 } 1005 } 1006 1007 /** 1008 * Add a derived plan from a given session. 1009 * 1010 * @param cp Command container. 1011 * @param name The plan name. 1012 * @param packageNames The package name list. 1013 */ 1014 private void addDerivedPlan(final CommandParser cp, final String name, 1015 ArrayList<String> packageNames) { 1016 1017 try { 1018 String sessionId = null; 1019 String resultType = null; 1020 int id = TestSession.getLastSessionId(); 1021 1022 if (cp.containsKey(CTSCommand.OPTION_SESSION)) { 1023 sessionId = cp.getValue(CTSCommand.OPTION_SESSION); 1024 id = Integer.parseInt(sessionId); 1025 } 1026 TestSession ts = mHost.getSession(id); 1027 if (ts == null) { 1028 Log.e("The session ID of " + id + " doesn't exist.", null); 1029 return; 1030 } 1031 1032 if (cp.containsKey(CTSCommand.OPTION_RESULT)) { 1033 resultType = cp.getValue(CTSCommand.OPTION_RESULT); 1034 if (!CtsTestResult.isValidResultType(resultType)) { 1035 Log.e("The following result type is invalid: " + resultType, null); 1036 return; 1037 } 1038 } 1039 createPlanFromSession(name, ts, resultType); 1040 } catch (Exception e) { 1041 Log.e("Got exception while trying to add a plan!", e); 1042 return; 1043 } 1044 } 1045 1046 /** 1047 * Add a plan by the plan name given. 1048 * 1049 * @param cp Command container. 1050 * @param name The plan name. 1051 * @param packageNames The package name list. 1052 */ 1053 private void addPlan(final CommandParser cp, final String name, 1054 ArrayList<String> packageNames) { 1055 1056 try { 1057 PlanBuilder planBuilder = new PlanBuilder(packageNames); 1058 1059 if (mOsName.equals(OS_NAME_LINUX)) { 1060 // planBuilder.setInputStream(sConsoleReader); 1061 } else { 1062 planBuilder.setInputStream(mCommandInput); 1063 } 1064 1065 HashMap<String, ArrayList<String>> selectedResult = planBuilder.doSelect(); 1066 if (selectedResult != null) { 1067 TestSessionBuilder.getInstance().serialize(name, packageNames, selectedResult); 1068 } else { 1069 Log.i("Selected nothing for the plan of " + name + ". The plan is not created!"); 1070 } 1071 } catch (Exception e) { 1072 Log.e("Got exception while trying to add a plan!", e); 1073 return; 1074 } 1075 } 1076 1077 /** 1078 * Create a plan. 1079 * 1080 * @param cp Command container. 1081 * @param type the action type. 1082 */ 1083 private void createPlan(final CommandParser cp, final String type) { 1084 String name = null; 1085 if (CTSCommand.OPTION_PLAN.equals(type)) { 1086 name = cp.getValue(CTSCommand.OPTION_PLAN); 1087 } else if (CTSCommand.OPTION_DERIVED_PLAN.equals(type)) { 1088 name = cp.getValue(CTSCommand.OPTION_DERIVED_PLAN); 1089 } else { 1090 return; 1091 } 1092 1093 if (HostUtils.isFileExist(HostConfig.getInstance().getPlanRepository() 1094 .getPlanPath(name)) == true) { 1095 Log.e("Plan " + name + " already exist, please use another name!", null); 1096 return; 1097 } 1098 1099 try { 1100 if ((name != null) && (!name.matches("\\w+"))) { 1101 CUIOutputStream.println("Only letter of the alphabet, number and '_'" 1102 + " are available for test plan name"); 1103 return; 1104 } 1105 1106 ArrayList<String> packageNames = 1107 HostConfig.getInstance().getCaseRepository().getPackageNames(); 1108 Collection<TestPackage> testPackages = HostConfig.getInstance().getTestPackages(); 1109 if (testPackages.size() == 0) { 1110 CUIOutputStream.println("No package found in repository, please add package first!"); 1111 return; 1112 } 1113 if (CTSCommand.OPTION_PLAN.equals(type)) { 1114 addPlan(cp, name, packageNames); 1115 } else if (CTSCommand.OPTION_DERIVED_PLAN.equals(type)) { 1116 addDerivedPlan(cp, name, packageNames); 1117 } 1118 } catch (Exception e) { 1119 Log.e("Got exception while trying to add a plan!", e); 1120 return; 1121 } 1122 } 1123 1124 /** 1125 * List all of the plans in the plan repository. 1126 */ 1127 private void listPlans() { 1128 ArrayList<String> plans = mHost.getPlanRepository().getAllPlanNames(); 1129 1130 if (plans.size() == 0) { 1131 CUIOutputStream.println("No plan created!"); 1132 } else { 1133 CUIOutputStream.println("List of plans (" + plans.size() + " in total):"); 1134 for (String name : plans) { 1135 CUIOutputStream.println(name); 1136 } 1137 } 1138 } 1139 1140 /** 1141 * List detailed case result of specified session. The result can be 1142 * filtered, if resultType isn't null, by the specified resultType. 1143 * 1144 * @param idStr the session id. 1145 * @param resultType the type of result, [pass, fail, notExecuted, timeout, null]. 1146 */ 1147 private void listSessionResult(final String idStr, final Integer resultType) { 1148 if (!idStr.matches("\\d+")) { 1149 showResultCmdHelp(); 1150 return; 1151 } 1152 1153 int sessionId = Integer.parseInt(idStr); 1154 1155 TestSession ts = mHost.getSession(sessionId); 1156 if (null == ts) { 1157 Log.e("Can't find specified session", null); 1158 return; 1159 } 1160 1161 TestSessionLog log = ts.getSessionLog(); 1162 CUIOutputStream.println("Result of session " + ts.getId()); 1163 CUIOutputStream.println("Result\t\tCase name"); 1164 CUIOutputStream 1165 .println("=============================================================="); 1166 for (Test test : log.getAllResults()) { 1167 CtsTestResult result = test.getResult(); 1168 if ((resultType != null) && (result.getResultCode() != resultType.intValue())) { 1169 continue; 1170 } 1171 CUIOutputStream.println(result.getResultString() + "\t\t" 1172 + test.getFullName()); 1173 } 1174 } 1175 1176 /** 1177 * List all of the test results. 1178 */ 1179 private void listResults() { 1180 Collection<TestSession> sessions = mHost.getSessions(); 1181 if (sessions.isEmpty()) { 1182 CUIOutputStream.println("There aren't any test results!"); 1183 } else { 1184 CUIOutputStream.println("List of all results: "); 1185 CUIOutputStream.println("Session\t\tTest result\t\t\t\tStart time\t\tEnd time\t" 1186 + "\tTest plan name\t"); 1187 CUIOutputStream.println("\t\tPass\tFail\tTimeout\tNotExecuted"); 1188 1189 for (TestSession session : sessions) { 1190 TestSessionLog log = session.getSessionLog(); 1191 int passNum = log.getTestList( 1192 CtsTestResult.CODE_PASS).size(); 1193 int failNum = log.getTestList( 1194 CtsTestResult.CODE_FAIL).size(); 1195 int notExecutedNum = log.getTestList( 1196 CtsTestResult.CODE_NOT_EXECUTED).size(); 1197 int timeOutNum = log.getTestList( 1198 CtsTestResult.CODE_TIMEOUT).size(); 1199 1200 String resStr = Long.toString(passNum) + "\t" + failNum; 1201 resStr += "\t" + timeOutNum; 1202 resStr += "\t" + notExecutedNum; 1203 1204 String startTimeStr = 1205 HostUtils.getFormattedTimeString(log.getStartTime().getTime(), " ", ".", ":"); 1206 String endTimeStr = 1207 HostUtils.getFormattedTimeString(log.getEndTime().getTime(), " ", ".", ":"); 1208 CUIOutputStream.println(Long.toString(session.getId()) + "\t\t" 1209 + resStr + "\t\t" + startTimeStr 1210 + "\t" + endTimeStr 1211 + "\t" + log.getTestPlanName()); 1212 1213 } 1214 } 1215 } 1216 1217 /** 1218 * Add a package by the path and package name. 1219 * 1220 * @param cp Command container. 1221 */ 1222 private void addPackage(final CommandParser cp) throws IOException, 1223 IndexOutOfBoundsException, NoSuchAlgorithmException { 1224 if (cp.getActionValues().size() != 0 || cp.getOptionSize() != 1) { 1225 showPackageCmdHelp(); 1226 return; 1227 } 1228 String pathName = cp.getValue(CTSCommand.OPTION_PACKAGE); 1229 mHost.addPackage(pathName); 1230 } 1231 1232 /** 1233 * List current package in the case repository. 1234 * 1235 * @param cp Command container 1236 */ 1237 private void listPackages(final CommandParser cp) { 1238 // walk through the case root path 1239 // and list available packages 1240 String expectPackage = cp.getValue(CTSCommand.OPTION_PACKAGE); 1241 String caseRoot = mHost.getCaseRepository().getRoot(); 1242 if (caseRoot == null) { 1243 Log.e("Case repository is null", null); 1244 return; 1245 } 1246 1247 File root = new File(caseRoot); 1248 if (!root.isDirectory()) { 1249 Log.e("Case repository must be a directory!", null); 1250 return; 1251 } 1252 1253 Collection<TestPackage> testPackages = HostConfig.getInstance().getTestPackages(); 1254 1255 if (testPackages.size() == 0) { 1256 CUIOutputStream 1257 .println("No package available under case repository!"); 1258 } else { 1259 if (expectPackage.equals("")) { 1260 CUIOutputStream.println("Available packages (" 1261 + testPackages.size() + " in total):"); 1262 for (TestPackage pkg : testPackages) { 1263 CUIOutputStream.println(pkg.getAppPackageName()); 1264 } 1265 } else { 1266 List<ArrayList<String>> list = mHost.getCaseRepository() 1267 .listAvailablePackage(expectPackage); 1268 ArrayList<String> packageList = list.get(0); 1269 ArrayList<String> suiteList = list.get(1); 1270 ArrayList<String> caseList = list.get(2); 1271 ArrayList<String> testList = list.get(3); 1272 if ((packageList.size() == 0) && (suiteList.size() == 0) 1273 && (caseList.size() == 0) && (testList.size() == 0)) { 1274 CUIOutputStream 1275 .println("Not available test package, suite, cases or tests: " 1276 + expectPackage); 1277 } else { 1278 if (packageList.size() != 0) { 1279 CUIOutputStream.println( 1280 "Test packages (" + packageList.size() + " in total):"); 1281 for (String packageName : packageList) { 1282 CUIOutputStream.println(packageName); 1283 } 1284 } 1285 if (suiteList.size() != 0) { 1286 CUIOutputStream.println( 1287 "Test suites (" + suiteList.size() + " in total):"); 1288 for (String suiteName : suiteList) { 1289 CUIOutputStream.println(suiteName); 1290 } 1291 } 1292 if (caseList.size() != 0) { 1293 CUIOutputStream.println("Test cases (" + caseList.size() + " in total):"); 1294 for (String caseName : caseList) { 1295 CUIOutputStream.println(caseName); 1296 } 1297 } 1298 if (testList.size() != 0) { 1299 CUIOutputStream.println("Tests (" + testList.size() + " in total):"); 1300 for (String testName : testList) { 1301 CUIOutputStream.println(testName); 1302 } 1303 } 1304 } 1305 } 1306 } 1307 } 1308 1309 /** 1310 * List all of the devices connected. 1311 */ 1312 private void listDevices() { 1313 String[] deviceNames = mHost.listDevices(); 1314 if (deviceNames.length == 0) { 1315 CUIOutputStream.println("No device connected."); 1316 return; 1317 } 1318 1319 CUIOutputStream.println("Id\t\tDevice Name\t\tStatus"); 1320 1321 for (int i = 0; i < deviceNames.length; i++) { 1322 CUIOutputStream.println(i + "\t\t" + deviceNames[i]); 1323 } 1324 } 1325 } 1326