# Copyright 2018, 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.

"""
Classes for test mapping related objects
"""


import copy
import os

import constants


class TestDetail(object):
    """Stores the test details set in a TEST_MAPPING file."""

    def __init__(self, details):
        """TestDetail constructor

        Parse test detail from a dictionary, e.g.,
        {
          "name": "SettingsUnitTests",
          "host": true,
          "options": [
            {
              "instrumentation-arg":
                  "annotation=android.platform.test.annotations.Presubmit"
            }
          ]
        }

        Args:
            details: A dictionary of test detail.
        """
        self.name = details['name']
        self.options = []
        # True if the test should run on host and require no device.
        self.host = details.get('host', False)
        assert isinstance(self.host, bool), 'host can only have boolean value.'
        options = details.get('options', [])
        for option in options:
            assert len(option) == 1, 'Each option can only have one key.'
            self.options.append(copy.deepcopy(option).popitem())
        self.options.sort(key=lambda o: o[0])

    def __str__(self):
        """String value of the TestDetail object."""
        host_info = (', runs on host without device required.' if self.host
                     else '')
        if not self.options:
            return self.name + host_info
        options = ''
        for option in self.options:
            options += '%s: %s, ' % option

        return '%s (%s)%s' % (self.name, options.strip(', '), host_info)

    def __hash__(self):
        """Get the hash of TestDetail based on the details"""
        return hash(str(self))

    def __eq__(self, other):
        return str(self) == str(other)


class Import(object):
    """Store test mapping import details."""

    def __init__(self, test_mapping_file, details):
        """Import constructor

        Parse import details from a dictionary, e.g.,
        {
            "path": "..\folder1"
        }
        in which, project is the name of the project, by default it's the
        current project of the containing TEST_MAPPING file.

        Args:
            test_mapping_file: Path to the TEST_MAPPING file that contains the
                import.
            details: A dictionary of details about importing another
                TEST_MAPPING file.
        """
        self.test_mapping_file = test_mapping_file
        self.path = details['path']

    def __str__(self):
        """String value of the Import object."""
        return 'Source: %s, path: %s' % (self.test_mapping_file, self.path)

    def get_path(self):
        """Get the path to TEST_MAPPING import directory."""
        path = os.path.realpath(os.path.join(
            os.path.dirname(self.test_mapping_file), self.path))
        if os.path.exists(path):
            return path
        root_dir = os.environ.get(constants.ANDROID_BUILD_TOP, os.sep)
        path = os.path.realpath(os.path.join(root_dir, self.path))
        if os.path.exists(path):
            return path
        # The import path can't be located.
        return None