# Copyright 2014 Cloudbase Solutions Srl
# All Rights Reserved.
#
#    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 mock

from unittest import mock

import ddt
import six

from os_win import constants
from os_win import exceptions
from os_win.tests.unit import test_base
from os_win.utils.io import ioutils
from os_win.utils.winapi import constants as w_const
from os_win.utils.winapi import wintypes


@ddt.ddt
class IOUtilsTestCase(test_base.BaseTestCase):

    _autospec_classes = [
        ioutils.win32utils.Win32Utils,
    ]

    def setUp(self):
        super(IOUtilsTestCase, self).setUp()
        self._setup_lib_mocks()

        self._ioutils = ioutils.IOUtils()

        self._mock_run = self._ioutils._win32_utils.run_and_check_output
        self._run_args = dict(kernel32_lib_func=True,
                              failure_exc=exceptions.Win32IOException,
                              eventlet_nonblocking_mode=False)

        self.addCleanup(mock.patch.stopall)

    def _setup_lib_mocks(self):
        self._ctypes = mock.Mock()
        # This is used in order to easily make assertions on the variables
        # passed by reference.
        self._ctypes.byref = lambda x: (x, "byref")
        self._ctypes.c_wchar_p = lambda x: (x, "c_wchar_p")

        mock.patch.multiple(ioutils,
                            ctypes=self._ctypes, kernel32=mock.DEFAULT,
                            create=True).start()

    def test_run_and_check_output(self):
        ret_val = self._ioutils._run_and_check_output(
            mock.sentinel.func, mock.sentinel.arg)

        self._mock_run.assert_called_once_with(mock.sentinel.func,
                                               mock.sentinel.arg,
                                               **self._run_args)
        self.assertEqual(self._mock_run.return_value, ret_val)

    @ddt.data({},
              {'inherit_handle': True},
              {'sec_attr': mock.sentinel.sec_attr})
    @ddt.unpack
    @mock.patch.object(wintypes, 'HANDLE')
    @mock.patch.object(wintypes, 'SECURITY_ATTRIBUTES')
    def test_create_pipe(self, mock_sec_attr_cls, mock_handle_cls,
                         inherit_handle=False, sec_attr=None):
        r, w = self._ioutils.create_pipe(
            sec_attr, mock.sentinel.size, inherit_handle)

        exp_sec_attr = None
        if sec_attr:
            exp_sec_attr = sec_attr
        elif inherit_handle:
            exp_sec_attr = mock_sec_attr_cls.return_value

        self.assertEqual(mock_handle_cls.return_value.value, r)
        self.assertEqual(mock_handle_cls.return_value.value, w)

        self._mock_run.assert_called_once_with(
            ioutils.kernel32.CreatePipe,
            self._ctypes.byref(mock_handle_cls.return_value),
            self._ctypes.byref(mock_handle_cls.return_value),
            self._ctypes.byref(exp_sec_attr) if exp_sec_attr else None,
            mock.sentinel.size,
            **self._run_args)

        if not sec_attr and exp_sec_attr:
            self.assertEqual(inherit_handle, exp_sec_attr.bInheritHandle)
            self.assertEqual(self._ctypes.sizeof.return_value,
                             exp_sec_attr.nLength)
            self._ctypes.sizeof.assert_called_once_with(exp_sec_attr)

    def test_wait_named_pipe(self):
        fake_timeout_s = 10