#!/usr/bin/python2.4
#
#
# Copyright 2009, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Utility to parse suite info from xml."""

# Python imports
import xml.dom.minidom
import xml.parsers

# local imports
import errors
import logger
import host_test
import instrumentation_test
import native_test


class XmlSuiteParser(object):
  """Parses XML attributes common to all TestSuite's."""

  # common attributes
  _NAME_ATTR = 'name'
  _BUILD_ATTR = 'build_path'
  _CONTINUOUS_ATTR = 'continuous'
  _SUITE_ATTR = 'suite'
  _DESCRIPTION_ATTR = 'description'
  _EXTRA_BUILD_ARGS_ATTR = 'extra_build_args'
  _FULL_MAKE_ATTR = 'full_make'
  _GRANTED_PERMISSIONS_ATTR = 'granted_permissions'

  def Parse(self, element):
    """Populates common suite attributes from given suite xml element.

    Args:
      element: xml node to parse
    Raises:
      ParseError if a required attribute is missing.
    Returns:
      parsed test suite or None
    """
    parser = None
    if element.nodeName == InstrumentationParser.TAG_NAME:
      parser = InstrumentationParser()
    elif element.nodeName == NativeParser.TAG_NAME:
      parser = NativeParser()
    elif element.nodeName == HostParser.TAG_NAME:
      parser = HostParser()
    else:
      logger.Log('Unrecognized tag %s found' % element.nodeName)
      return None
    test_suite = parser.Parse(element)
    return test_suite

  def _ParseCommonAttributes(self, suite_element, test_suite):
    test_suite.SetName(self._ParseAttribute(suite_element, self._NAME_ATTR,
                                            True))
    test_suite.SetBuildPath(self._ParseAttribute(suite_element,
                                                 self._BUILD_ATTR, True))
    test_suite.SetContinuous(self._ParseAttribute(suite_element,
                                                  self._CONTINUOUS_ATTR,
                                                  False, default_value=False))
    test_suite.SetIsGrantedPermissions(self._ParseAttribute(suite_element,
                                                  self._GRANTED_PERMISSIONS_ATTR,
                                                  False, default_value=True))
    test_suite.SetSuite(self._ParseAttribute(suite_element, self._SUITE_ATTR, False,
                                           default_value=None))
    test_suite.SetDescription(self._ParseAttribute(suite_element,
                                                   self._DESCRIPTION_ATTR,
                                                   False,
                                                   default_value=''))
    test_suite.SetExtraBuildArgs(self._ParseAttribute(
        suite_element, self._EXTRA_BUILD_ARGS_ATTR, False, default_value=''))
    test_suite.SetIsFullMake(self._ParseAttribute(
        suite_element, self._FULL_MAKE_ATTR, False, default_value=False))


  def _ParseAttribute(self, suite_element, attribute_name, mandatory,
                      default_value=None):
    if suite_element.hasAttribute(attribute_name):
      value = suite_element.getAttribute(attribute_name)
      if default_value in (True, False):
        value = value.lower() == "true"
    elif mandatory:
      error_msg = ('Could not find attribute %s in %s' %
                   (attribute_name, self.TAG_NAME))
      raise errors.ParseError(msg=error_msg)
    else:
      value = default_value
    return value


class InstrumentationParser(XmlSuiteParser):
  """Parses instrumentation suite attributes from xml."""

  # for legacy reasons, the xml tag name for java (device) tests is 'test'
  TAG_NAME = 'test'

  _PKG_ATTR = 'package'
  _RUNNER_ATTR = 'runner'
  _CLASS_ATTR = 'class'
  _TARGET_ATTR = 'coverage_target'

  def Parse(self, suite_element):
    """Creates suite and populate with data from xml element."""
    suite = instrumentation_test.InstrumentationTestSuite()
    XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
    suite.SetPackageName(self._ParseAttribute(suite_element, self._PKG_ATTR,
                                              True))
    suite.SetRunnerName(self._ParseAttribute(
        suite_element, self._RUNNER_ATTR, False,
        instrumentation_test.InstrumentationTestSuite.DEFAULT_RUNNER))
    suite.SetClassName(self._ParseAttribute(suite_element, self._CLASS_ATTR,
                                            False))
    suite.SetTargetName(self._ParseAttribute(suite_element, self._TARGET_ATTR,
                                             False))
    return suite


class NativeParser(XmlSuiteParser):
  """Parses native suite attributes from xml."""

  TAG_NAME = 'test-native'

  def Parse(self, suite_element):
    """Creates suite and populate with data from xml element."""
    suite = native_test.NativeTestSuite()
    XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
    return suite


class HostParser(XmlSuiteParser):
  """Parses host suite attributes from xml."""

  TAG_NAME = 'test-host'

  _CLASS_ATTR = 'class'
  # TODO: consider obsoleting in favor of parsing the Android.mk to find the
  # jar name
  _JAR_ATTR = 'jar_name'

  def Parse(self, suite_element):
    """Creates suite and populate with data from xml element."""
    suite = host_test.HostTestSuite()
    XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
    suite.SetClassName(self._ParseAttribute(suite_element, self._CLASS_ATTR,
                                            True))
    suite.SetJarName(self._ParseAttribute(suite_element, self._JAR_ATTR, True))
    return suite