#!/usr/bin/python3
# -*- coding: utf-8 -*-

from io import StringIO
from unittest.mock import MagicMock, patch

from shared import REAL_GET_OS_INFO


_CPUINFO_4_CORES = (
    'processor\t: 0\nphysical id\t: 0\ncore id\t: 0\n\n'
    'processor\t: 1\nphysical id\t: 0\ncore id\t: 1\n\n'
    'processor\t: 2\nphysical id\t: 0\ncore id\t: 2\n\n'
    'processor\t: 3\nphysical id\t: 0\ncore id\t: 3\n\n'
)
_MEMINFO = 'MemTotal: 2048 kB\nMemFree: 1024 kB\nMemAvailable: 1536 kB\n'
_MOUNTS_EXT4 = '/dev/sda1 / ext4 rw 0 0\n'
_MOUNTS_EMPTY = 'tmpfs /tmp tmpfs rw 0 0\n'


def _make_open(os_release='PRETTY_NAME="Slackware 15.0"\n', mounts=_MOUNTS_EXT4):
    files = {
        '/etc/os-release': os_release,
        '/proc/uptime': '3600.0 1234.5\n',
        '/proc/cpuinfo': _CPUINFO_4_CORES,
        '/proc/meminfo': _MEMINFO,
        '/proc/mounts': mounts,
    }

    def _open(path, *args, **kwargs):
        return StringIO(files[str(path)])

    return _open


def _make_statvfs():
    mock_statvfs = MagicMock()
    mock_statvfs.f_blocks = 100
    mock_statvfs.f_bfree = 50
    mock_statvfs.f_bavail = 50
    mock_statvfs.f_frsize = 1
    return mock_statvfs


def _get_os_info_with_mocks(os_release='PRETTY_NAME="Slackware 15.0"\n',
                            lspci_stdout='', mounts=_MOUNTS_EXT4,
                            lspci_side_effect=None, open_side_effect=None):
    run_mock = (
        {'side_effect': lspci_side_effect} if lspci_side_effect
        else {'return_value': MagicMock(stdout=lspci_stdout, returncode=0)}
    )
    open_mock = (
        {'side_effect': open_side_effect} if open_side_effect
        else {'side_effect': _make_open(os_release, mounts)}
    )

    with patch('builtins.open', **open_mock), \
         patch('subprocess.run', **run_mock), \
         patch('platform.machine', return_value='x86_64'), \
         patch('socket.gethostname', return_value='testhost'), \
         patch('os.cpu_count', return_value=4), \
         patch('os.statvfs', return_value=_make_statvfs()):
        return REAL_GET_OS_INFO()


class TestGetOsInfo:

    def test_os_name(self):
        result = _get_os_info_with_mocks()
        assert result['os_name'] == 'Slackware 15.0'

    def test_arch(self):
        result = _get_os_info_with_mocks()
        assert result['arch'] == 'x86_64'

    def test_hostname(self):
        result = _get_os_info_with_mocks()
        assert result['hostname'] == 'testhost'

    def test_cpu_cores(self):
        result = _get_os_info_with_mocks()
        assert result['cpu_cores'] == 4

    def test_gpu_empty_when_no_vga(self):
        result = _get_os_info_with_mocks(lspci_stdout='')
        assert result['gpu'] == ''

    def test_disk_type(self):
        result = _get_os_info_with_mocks()
        assert result['disk_type'] == 'ext4'

    def test_no_disk_root_entry(self):
        result = _get_os_info_with_mocks(mounts=_MOUNTS_EMPTY)
        assert result['disk_type'] == ''

    def test_os_name_missing_pretty_name(self):
        result = _get_os_info_with_mocks(os_release='NAME="Slackware"\n')
        assert result['os_name'] == ''

    def test_os_release_oserror_returns_empty_os_name(self):
        def _open_no_release(path, *args, **kwargs):
            if str(path) == '/etc/os-release':
                raise OSError('No such file')
            return _make_open()(path, *args, **kwargs)
        result = _get_os_info_with_mocks(open_side_effect=_open_no_release)
        assert result['os_name'] == ''

    def test_lspci_not_found_returns_empty_gpu(self):
        result = _get_os_info_with_mocks(lspci_side_effect=FileNotFoundError)
        assert result['gpu'] == ''
