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 package com.android.cts; 17 18 import java.io.File; 19 import java.io.FileNotFoundException; 20 import java.io.IOException; 21 import java.security.NoSuchAlgorithmException; 22 import java.util.ArrayList; 23 import java.util.Collection; 24 import java.util.HashMap; 25 26 import javax.xml.parsers.DocumentBuilder; 27 import javax.xml.parsers.DocumentBuilderFactory; 28 import javax.xml.parsers.ParserConfigurationException; 29 import javax.xml.transform.TransformerException; 30 import javax.xml.transform.TransformerFactoryConfigurationError; 31 32 import org.w3c.dom.Document; 33 import org.w3c.dom.Node; 34 import org.w3c.dom.NodeList; 35 import org.xml.sax.SAXException; 36 37 /** 38 * Builder of test plan and also provides serialization for a test plan. 39 */ 40 public class TestSessionBuilder extends XMLResourceHandler { 41 // defined for external document, which is from the configuration files 42 // this should keep synchronized with the format of the configuration files 43 44 private static final String TAG_TEST_SUITE = "TestSuite"; 45 private static final String TAG_TEST_CASE = "TestCase"; 46 public static final String TAG_TEST = "Test"; 47 48 // attribute name define 49 public static final String ATTRIBUTE_SIGNATURE_CHECK = "signatureCheck"; 50 public static final String ATTRIBUTE_REFERENCE_APP_TEST = "referenceAppTest"; 51 public static final String ATTRIBUTE_PRIORITY = "priority"; 52 53 private static final String ATTRIBUTE_NAME = "name"; 54 private static final String ATTRIBUTE_RUNNER = "runner"; 55 private static final String ATTRIBUTE_JAR_PATH = "jarPath"; 56 private static final String ATTRIBUTE_APP_NAME_SPACE = "appNameSpace"; 57 public static final String ATTRIBUTE_APP_PACKAGE_NAME = "appPackageName"; 58 private static final String ATTRIBUTE_TARGET_NAME_SPACE = "targetNameSpace"; 59 private static final String ATTRIBUTE_TARGET_BINARY_NAME = "targetBinaryName"; 60 private static final String ATTRIBUTE_TYPE = "type"; 61 private static final String ATTRIBUTE_CONTROLLER = "HostController"; 62 private static final String ATTRIBUTE_KNOWN_FAILURE = "KnownFailure"; 63 private static final String ATTRIBUTE_HOST_SIDE_ONLY = "hostSideOnly"; 64 private static final String ATTRIBUTE_VERSION = "version"; 65 private static final String ATTRIBUTE_FRAMEWORK_VERSION = "AndroidFramework"; 66 private static final String ATTRIBUTE_APK_TO_TEST_NAME = "apkToTestName"; 67 private static final String ATTRIBUTE_PACKAGE_TO_TEST = "packageToTest"; 68 private static TestSessionBuilder sInstance; 69 70 private DocumentBuilder mDocBuilder; 71 72 public static TestSessionBuilder getInstance() 73 throws ParserConfigurationException { 74 if (sInstance == null) { 75 sInstance = new TestSessionBuilder(); 76 } 77 78 return sInstance; 79 } 80 81 private TestSessionBuilder() throws ParserConfigurationException { 82 mDocBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 83 } 84 85 /** 86 * Create TestSession via TestSessionLog. 87 * 88 * @param log The test session log. 89 * @return The test session. 90 */ 91 public TestSession build(TestSessionLog log) { 92 if (log == null) { 93 return null; 94 } 95 return new TestSession(log, 1); 96 } 97 98 /** 99 * Create TestSession via TestPlan XML configuration file. 100 * 101 * @param config TestPlan XML configuration file. 102 * @return TestSession. 103 */ 104 public TestSession build(final String config) throws SAXException, IOException, 105 TestPlanNotFoundException, TestNotFoundException, NoSuchAlgorithmException { 106 File file = new File(config); 107 if (!file.exists()) { 108 throw new TestPlanNotFoundException(); 109 } 110 Document doc = mDocBuilder.parse(file); 111 112 // parse device configuration 113 int numOfRequiredDevices = 1; // default is 1 114 try { 115 Node deviceConfigNode = doc.getElementsByTagName( 116 TestPlan.Tag.REQUIRED_DEVICE).item(0); 117 numOfRequiredDevices = getAttributeValue(deviceConfigNode, 118 TestPlan.Attribute.AMOUNT); 119 } catch (Exception e) { 120 } 121 122 Collection<TestPackage> packages = loadPackages(doc); 123 if (packages.size() == 0) { 124 throw new TestNotFoundException("No valid package in test plan."); 125 } 126 127 String planFileName = file.getName(); 128 int index = planFileName.indexOf("."); 129 String planName; 130 if (index != -1) { 131 planName = planFileName.substring(0, planFileName.indexOf(".")); 132 } else{ 133 planName = planFileName; 134 } 135 136 TestSessionLog sessionLog = new TestSessionLog(packages, planName); 137 TestSession ts = new TestSession(sessionLog, numOfRequiredDevices); 138 return ts; 139 } 140 141 /** 142 * Load TestPackages from a TestPlan DOM doc. 143 * 144 * @param doc TestPlan DOM Document 145 * @return loaded test package from TestPlan DOM Document 146 */ 147 private Collection<TestPackage> loadPackages(Document doc) 148 throws SAXException, IOException, NoSuchAlgorithmException { 149 150 ArrayList<TestPackage> packages = new ArrayList<TestPackage>(); 151 NodeList packageList = doc.getElementsByTagName(TestPlan.Tag.ENTRY); 152 ArrayList<String> removedPkgList = new ArrayList<String>(); 153 for (int i = 0; i < packageList.getLength(); i++) { 154 Node pNode = packageList.item(i); 155 String uri = getStringAttributeValue(pNode, TestPlan.Attribute.URI); 156 String list = getStringAttributeValue(pNode, TestPlan.Attribute.EXCLUDE); 157 ArrayList<String> excludedList = null; 158 if ((list != null) && (list.length() != 0)) { 159 excludedList = getStrArrayList(list); 160 } 161 162 String packageBinaryName = HostConfig.getInstance().getPackageBinaryName(uri); 163 if (packageBinaryName != null) { 164 String xmlConfigFilePath = 165 HostConfig.getInstance().getCaseRepository().getXmlPath(packageBinaryName); 166 File xmlFile = new File(xmlConfigFilePath); 167 TestPackage pkg = loadPackage(xmlFile, excludedList); 168 if (pkg instanceof SignatureCheckPackage) { 169 // insert the signature check package 170 // to the head of the list 171 packages.add(0, pkg); 172 } else { 173 packages.add(pkg); 174 } 175 } else{ 176 removedPkgList.add(uri); 177 } 178 } 179 if (removedPkgList.size() != 0) { 180 CUIOutputStream.println("The following package(s) doesn't exist:"); 181 for (String pkgName : removedPkgList) { 182 CUIOutputStream.println(" " + pkgName); 183 } 184 } 185 return packages; 186 } 187 188 /** 189 * Load TestPackage via Package XML configuration file. 190 * 191 * @param packageConfigFile test package XML file 192 * @param excludedList The list containing the excluded suites and sub types. 193 * @return loaded TestPackage from test package XML configuration file 194 */ 195 public TestPackage loadPackage(final File packageConfigFile, ArrayList<String> excludedList) 196 throws SAXException, IOException, NoSuchAlgorithmException { 197 Node pNode = mDocBuilder.parse(packageConfigFile).getDocumentElement(); 198 return loadPackage(pNode, excludedList); 199 } 200 201 /** 202 * Load TestPackage via Package XML configuration file. 203 * 204 * @param pkgNode the test package node in the XML file 205 * @param excludedList The list containing the excluded suites and sub types. 206 * @return loaded TestPackage from test package XML configuration file 207 */ 208 public TestPackage loadPackage(final Node pkgNode, ArrayList<String> excludedList) 209 throws NoSuchAlgorithmException { 210 211 String appBinaryName, targetNameSpace, targetBinaryName, version, frameworkVersion, 212 runner, jarPath, appNameSpace, appPackageName, hostSideOnly; 213 NodeList suiteList = pkgNode.getChildNodes(); 214 215 appBinaryName = getStringAttributeValue(pkgNode, ATTRIBUTE_NAME); 216 targetNameSpace = getStringAttributeValue(pkgNode, ATTRIBUTE_TARGET_NAME_SPACE); 217 targetBinaryName = getStringAttributeValue(pkgNode, ATTRIBUTE_TARGET_BINARY_NAME); 218 version = getStringAttributeValue(pkgNode, ATTRIBUTE_VERSION); 219 frameworkVersion = getStringAttributeValue(pkgNode, ATTRIBUTE_FRAMEWORK_VERSION); 220 runner = getStringAttributeValue(pkgNode, ATTRIBUTE_RUNNER); 221 jarPath = getStringAttributeValue(pkgNode, ATTRIBUTE_JAR_PATH); 222 appNameSpace = getStringAttributeValue(pkgNode, ATTRIBUTE_APP_NAME_SPACE); 223 appPackageName = getStringAttributeValue(pkgNode, ATTRIBUTE_APP_PACKAGE_NAME); 224 hostSideOnly = getStringAttributeValue(pkgNode, ATTRIBUTE_HOST_SIDE_ONLY); 225 String signature = getStringAttributeValue(pkgNode, ATTRIBUTE_SIGNATURE_CHECK); 226 String referenceAppTest = getStringAttributeValue(pkgNode, ATTRIBUTE_REFERENCE_APP_TEST); 227 TestPackage pkg = null; 228 229 if ("true".equals(referenceAppTest)) { 230 String apkToTestName = getStringAttributeValue(pkgNode, ATTRIBUTE_APK_TO_TEST_NAME); 231 String packageUnderTest = getStringAttributeValue(pkgNode, ATTRIBUTE_PACKAGE_TO_TEST); 232 pkg = new ReferenceAppTestPackage(runner, appBinaryName, targetNameSpace, 233 targetBinaryName, version, frameworkVersion, jarPath, 234 appNameSpace, appPackageName, 235 apkToTestName, packageUnderTest); 236 } else if ("true".equals(signature)) { 237 pkg = new SignatureCheckPackage(runner, appBinaryName, targetNameSpace, 238 targetBinaryName, version, frameworkVersion, jarPath, 239 appNameSpace, appPackageName); 240 } else if ("true".equals(hostSideOnly)) { 241 pkg = new HostSideOnlyPackage(appBinaryName, version, frameworkVersion, 242 jarPath, appPackageName); 243 } else { 244 pkg = new TestPackage(runner, appBinaryName, targetNameSpace, targetBinaryName, 245 version, frameworkVersion, jarPath, appNameSpace, appPackageName); 246 } 247 248 for (int i = 0; i < suiteList.getLength(); i++) { 249 Node sNode = suiteList.item(i); 250 if (sNode.getNodeType() == Document.ELEMENT_NODE 251 && TAG_TEST_SUITE.equals(sNode.getNodeName())) { 252 String fullSuiteName = getFullSuiteName(sNode); 253 if (checkFullMatch(excludedList, fullSuiteName) == false) { 254 ArrayList<String> excludedCaseList = 255 getExcludedList(excludedList, fullSuiteName); 256 TestSuite suite = loadSuite(pkg, sNode, excludedCaseList); 257 if ((suite.getTestCases().size() != 0) || (suite.getSubSuites().size() != 0)) { 258 pkg.addTestSuite(suite); 259 } 260 } else { 261 Log.d("suite=" + fullSuiteName + " is fully excluded"); 262 } 263 } 264 } 265 266 return pkg; 267 } 268 269 /** 270 * Get string ArrayList from string. 271 * 272 * @param str The given string. 273 * @return The list. 274 */ 275 private ArrayList<String> getStrArrayList(String str) { 276 if ((str == null) || (str.length() == 0)) { 277 return null; 278 } 279 280 String[] list = str.split(TestPlan.EXCLUDE_SEPARATOR); 281 if ((list == null) || (list.length == 0)) { 282 return null; 283 } 284 285 ArrayList<String> result = new ArrayList<String>(); 286 for (String s : list) { 287 result.add(s); 288 } 289 290 return result; 291 } 292 293 /** 294 * Get excluded list from a list by offered expectation. 295 * 296 * @param excludedList The list containing excluded items. 297 * @param expectation The expectations. 298 * @return The excluded list. 299 */ 300 private ArrayList<String> getExcludedList(ArrayList<String> excludedList, String expectation) { 301 if ((excludedList == null) || (excludedList.size() == 0)) { 302 return null; 303 } 304 305 ArrayList<String> list = new ArrayList<String>(); 306 for (String str : excludedList) { 307 if (str.startsWith(expectation)) { 308 list.add(str); 309 } 310 } 311 312 if (list.size() == 0) { 313 return null; 314 } else { 315 return list; 316 } 317 } 318 319 /** 320 * Check if the expectation is fully matched among a list. 321 * 322 * @param list The array list. 323 * @param expectation The expectation. 324 * @return If there is full match of expectation among the list, return true; 325 * else, return false. 326 */ 327 private boolean checkFullMatch(ArrayList<String> list, String expectation) { 328 if ((list == null) || (list.size() == 0)) { 329 return false; 330 } 331 332 for (String str : list) { 333 if (str.equals(expectation)) { 334 return true; 335 } 336 } 337 338 return false; 339 } 340 341 /** 342 * Load TestSuite via suite node. Package Name is used to output test result. 343 * 344 * @param pkg TestPackage 345 * @param sNode suite node 346 * @param excludedCaseList The list containing the excluded cases and sub types. 347 * @return TestSuite 348 */ 349 private TestSuite loadSuite(final TestPackage pkg, Node sNode, 350 ArrayList<String> excludedCaseList) { 351 NodeList cNodes = sNode.getChildNodes(); 352 String fullSuiteName = getFullSuiteName(sNode); 353 String suiteName = getStringAttributeValue(sNode, TestPlan.Attribute.NAME); 354 TestSuite suite = new TestSuite(pkg, suiteName, fullSuiteName); 355 356 for (int i = 0; i < cNodes.getLength(); i++) { 357 Node cNode = cNodes.item(i); 358 if (cNode.getNodeType() == Document.ELEMENT_NODE) { 359 if (cNode.getNodeName().equals(TAG_TEST_SUITE)) { 360 String subSuiteName = getFullSuiteName(cNode); 361 if (checkFullMatch(excludedCaseList, subSuiteName) == false) { 362 ArrayList<String> excludedList = getExcludedList(excludedCaseList, 363 subSuiteName); 364 TestSuite subSuite = loadSuite(pkg, cNode, excludedList); 365 if ((subSuite.getTestCases().size() != 0) 366 || (subSuite.getSubSuites().size() != 0)) { 367 suite.addSubSuite(subSuite); 368 } 369 } else { 370 Log.d("suite=" + subSuiteName + " is fully excluded"); 371 } 372 } else if (cNode.getNodeName().equals(TAG_TEST_CASE)) { 373 String cName = getStringAttributeValue(cNode, ATTRIBUTE_NAME); 374 String priority = getStringAttributeValue(cNode, ATTRIBUTE_PRIORITY); 375 376 TestCase testCase = new TestCase(suite, cName, priority); 377 String fullCaseName = fullSuiteName + "." + testCase.getName(); 378 if (checkFullMatch(excludedCaseList, fullCaseName) == false) { 379 NodeList mNodes = cNode.getChildNodes(); 380 for (int t = 0; t < mNodes.getLength(); t ++) { 381 Node testNode = mNodes.item(t); 382 if ((testNode.getNodeType() == Document.ELEMENT_NODE) 383 && (testNode.getNodeName().equals(TAG_TEST))) { 384 Test test = loadTest(pkg, testCase, testNode); 385 if (!checkFullMatch(excludedCaseList, test.getFullName())) { 386 testCase.addTest(test); 387 } else { 388 Log.d("Test=" + test.getFullName() + " is excluded"); 389 } 390 } 391 } 392 if (testCase.getTests().size() != 0) { 393 suite.addTestCase(testCase); 394 } 395 } else { 396 Log.d("case=" + fullCaseName + " is fully excluded"); 397 } 398 } 399 } 400 } 401 402 return suite; 403 } 404 405 /** 406 * Load test via test node. 407 * 408 * @param pkg The test package. 409 * @param testCase The test case. 410 * @param testNode The test node. 411 * @return The test loaded. 412 */ 413 private Test loadTest(final TestPackage pkg, TestCase testCase, 414 Node testNode) { 415 String cType = getStringAttributeValue(testNode, ATTRIBUTE_TYPE); 416 String name = getStringAttributeValue(testNode, ATTRIBUTE_NAME); 417 String description = getStringAttributeValue(testNode, 418 ATTRIBUTE_CONTROLLER); 419 String knownFailure = getStringAttributeValue(testNode, 420 ATTRIBUTE_KNOWN_FAILURE); 421 String fullJarPath = 422 HostConfig.getInstance().getCaseRepository().getRoot() 423 + File.separator + pkg.getJarPath(); 424 CtsTestResult testResult = loadTestResult(testNode); 425 Test test = null; 426 if (pkg.isHostSideOnly()) { 427 test = new HostSideOnlyTest(testCase, name, cType, 428 knownFailure, 429 CtsTestResult.CODE_NOT_EXECUTED); 430 description = test.getFullName(); 431 } else { 432 test = new Test(testCase, name, cType, 433 knownFailure, 434 CtsTestResult.CODE_NOT_EXECUTED); 435 } 436 437 TestController controller = 438 genTestControler(fullJarPath, description); 439 test.setTestController(controller); 440 if (testResult != null) { 441 test.addResult(testResult); 442 } 443 return test; 444 } 445 446 /** 447 * Load the CTS test result from the test node. 448 * 449 * @param testNode The test node. 450 * @return The CTS test result. 451 */ 452 private CtsTestResult loadTestResult(Node testNode) { 453 String result = getStringAttributeValue(testNode, 454 TestSessionLog.ATTRIBUTE_RESULT); 455 456 String failedMessage = null; 457 String stackTrace = null; 458 NodeList nodes = testNode.getChildNodes(); 459 for (int i = 0; i < nodes.getLength(); i ++) { 460 Node rNode = nodes.item(i); 461 if ((rNode.getNodeType() == Document.ELEMENT_NODE) 462 && (rNode.getNodeName().equals(TestSessionLog.TAG_FAILED_SCENE))) { 463 failedMessage = getStringAttributeValue(rNode, TestSessionLog.TAG_FAILED_MESSAGE); 464 stackTrace = getStringAttributeValue(rNode, TestSessionLog.TAG_STACK_TRACE); 465 if (stackTrace == null) { 466 NodeList sNodeList = rNode.getChildNodes(); 467 for (int j = 0; j < sNodeList.getLength(); j ++) { 468 Node sNode = sNodeList.item(i); 469 if ((sNode.getNodeType() == Document.ELEMENT_NODE) 470 && (sNode.getNodeName().equals(TestSessionLog.TAG_STACK_TRACE))) { 471 stackTrace = sNode.getTextContent(); 472 } 473 } 474 } 475 break; 476 } 477 } 478 479 CtsTestResult testResult = null; 480 if (result != null) { 481 try { 482 testResult = new CtsTestResult(result, failedMessage, stackTrace); 483 } catch (InvalidTestResultStringException e) { 484 } 485 } 486 487 return testResult; 488 } 489 490 /** 491 * Generate controller according to the description string. 492 * 493 * @return The test controller. 494 */ 495 public TestController genTestControler(String jarPath, String description) { 496 if ((jarPath == null) || (jarPath.length() == 0) 497 || (description == null) || (description.length() == 0)) { 498 return null; 499 } 500 501 String packageName = description.substring(0, description.lastIndexOf(".")); 502 String className = description.substring(packageName.length() + 1, 503 description.lastIndexOf(Test.METHOD_SEPARATOR)); 504 String methodName = description.substring( 505 description.lastIndexOf(Test.METHOD_SEPARATOR) + 1, 506 description.length()); 507 508 return new TestController(jarPath, packageName, className, methodName); 509 } 510 511 /** 512 * Get the full suite name of the specified suite node. Since the test 513 * suite can be nested, so the full name of a tests suite is combined 514 * with his name and his ancestor suite's names. 515 * 516 * @param node The specified suite node. 517 * @return The full name of the given suite node. 518 */ 519 private String getFullSuiteName(Node node) { 520 StringBuilder buf = new StringBuilder(); 521 buf.append(getStringAttributeValue(node, TestPlan.Attribute.NAME)); 522 523 Node parent = node.getParentNode(); 524 while (parent != null) { 525 if (parent.getNodeType() == Document.ELEMENT_NODE 526 && parent.getNodeName() == TAG_TEST_SUITE) { 527 buf.insert(0, "."); 528 buf.insert(0, getStringAttributeValue(parent, TestPlan.Attribute.NAME)); 529 } 530 531 parent = parent.getParentNode(); 532 } 533 534 return buf.toString(); 535 } 536 537 /** 538 * Create TestPlan which contain a series TestPackages. 539 * 540 * @param planName test plan name 541 * @param packageNames Package names to be added 542 * @param selectedResult The selected result mapping selected 543 * package with selected removal result. 544 */ 545 public void serialize(String planName, 546 ArrayList<String> packageNames, HashMap<String, ArrayList<String>> selectedResult) 547 throws ParserConfigurationException, FileNotFoundException, 548 TransformerFactoryConfigurationError, TransformerException { 549 File plan = new File(HostConfig.getInstance().getPlanRepository() 550 .getPlanPath(planName)); 551 if (plan.exists()) { 552 Log.e("Plan " + planName + " already exist, please use another name!", 553 null); 554 return; 555 } 556 557 Document doc = DocumentBuilderFactory.newInstance() 558 .newDocumentBuilder().newDocument(); 559 Node root = doc.createElement(TestPlan.Tag.TEST_PLAN); 560 setAttribute(doc, root, ATTRIBUTE_VERSION, "1.0"); 561 doc.appendChild(root); 562 563 // append device configure node 564 Node deviceConfigNode = doc.createElement(TestPlan.Tag.PLAN_SETTING); 565 566 root.appendChild(deviceConfigNode); 567 568 // append test packages 569 for (String pName : packageNames) { 570 if (selectedResult.containsKey(pName)) { 571 Node entryNode = doc.createElement(TestPlan.Tag.ENTRY); 572 573 setAttribute(doc, entryNode, TestPlan.Attribute.URI, pName); 574 ArrayList<String> excluded = selectedResult.get(pName); 575 if ((excluded != null) && (excluded.size() != 0)) { 576 String excludedList = ""; 577 for (String str : excluded) { 578 excludedList += str + TestPlan.EXCLUDE_SEPARATOR; 579 } 580 setAttribute(doc, entryNode, TestPlan.Attribute.EXCLUDE, excludedList); 581 } 582 root.appendChild(entryNode); 583 } 584 } 585 586 writeToFile(plan, doc); 587 } 588 589 } 590