# 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.

import fixtures
import logging
import os
from unittest import mock

import diskimage_builder.block_device.tests.test_config as tc

from diskimage_builder.block_device.blockdevice import BlockDeviceState
from diskimage_builder.block_device.config import config_tree_to_graph
from diskimage_builder.block_device.config import create_graph
from diskimage_builder.block_device.level0.localloop import image_create
from diskimage_builder.block_device.level1.partition import PartitionNode

logger = logging.getLogger(__name__)


class TestGPT(tc.TestGraphGeneration):

    @mock.patch('diskimage_builder.block_device.level1.partitioning.exec_sudo')
    def test_gpt_efi(self, mock_exec_sudo):
        # Test the command-sequence for a GPT/EFI partition setup
        tree = self.load_config_file('gpt_efi.yaml')
        config = config_tree_to_graph(tree)

        state = BlockDeviceState()

        graph, call_order = create_graph(config, self.fake_default_config,
                                         state)

        # Create a fake temp backing file (we check the size of it,
        # etc).
        # TODO(ianw): exec_sudo is generically mocked out, thus the
        # actual creation is mocked out ... but we could do this
        # without root and use parted to create the partitions on this
        # for slightly better testing.  An exercise for another day...
        self.tmp_dir = fixtures.TempDir()
        self.useFixture(self.tmp_dir)
        self.image_path = os.path.join(self.tmp_dir.path, "image.raw")
        # should be sparse...
        image_create(self.image_path, 1024 * 1024 * 1024)
        logger.debug("Temp image in %s", self.image_path)

        # Fake state for the loopback device
        state['blockdev'] = {}
        state['blockdev']['image0'] = {}
        state['blockdev']['image0']['image'] = self.image_path
        state['blockdev']['image0']['device'] = "/dev/loopX"

        for node in call_order:
            if isinstance(node, PartitionNode):
                node.create()

        # check the parted call looks right
        parted_cmd = ['sgdisk', self.image_path,
                      '-n', '1:0:+8M', '-t', '1:EF00', '-c', '1:ESP',
                      '-n', '2:0:+8M', '-t', '2:EF02', '-c', '2:BSP',
                      '-n', '3:0:+1006M', '-t', '3:8300', '-c', '3:Root Part']
        cmd_sequence = [
            mock.call(parted_cmd),
            mock.call(['sync']),
            mock.call(['kpartx', '-uvs', '/dev/loopX'])
        ]
        self.assertEqual(mock_exec_sudo.call_count, len(cmd_sequence))
        mock_exec_sudo.assert_has_calls(cmd_sequence)

        # Check two new partitions appear in state correctly
        self.assertDictEqual(state['blockdev']['ESP'],
                             {'device': '/dev/mapper/loopXp1'})
        self.assertDictEqual(state['blockdev']['BSP'],
                             {'device': '/dev/mapper/loopXp2'})
        self.assertDictEqual(state['blockdev']['Root Part'],
                             {'device': '/dev/mapper/loopXp3'})
