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