"""Unit test for cgroups module.
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import io
import os
import shutil
import tempfile
import unittest

import mock

import treadmill
from treadmill import cgroups
from treadmill import cgutils


PROCCGROUPS = """#subsys_name    hierarchy       num_cgroups     enabled
cpuset  4       1       0
ns      10      3       0
cpu     2       3       1
cpuacct 3       3       1
memory  7       3       1
devices 5       1       0
freezer 6       1       0
net_cls 8       1       0
blkio   1       1       0
perf_event      11      1       0
net_prio        9       1       0"""


class CGroupsTest(unittest.TestCase):
    """Tests for teadmill.cgroups."""

    def setUp(self):
        self.root = tempfile.mkdtemp()

    def tearDown(self):
        if self.root and os.path.isdir(self.root):
            shutil.rmtree(self.root)

    @mock.patch('treadmill.cgroups.get_data',
                mock.Mock(side_effect=['2', '1\n2', '-1', '']))
    def test_get_value(self):
        """Test cgroup value fetching"""
        value = cgroups.get_value('memory', 'foo', 'memory,usage_in_bytes')
        self.assertEqual(value, 2)

        value = cgroups.get_value('memory', 'foo', 'memory,usage_in_bytes')
        self.assertEqual(value, 1)

        value = cgroups.get_value('memory', 'foo', 'memory,usage_in_bytes')
        self.assertEqual(value, 0)

        value = cgroups.get_value('memory', 'foo', 'memory,usage_in_bytes')
        self.assertEqual(value, 0)

    @mock.patch('treadmill.cgroups.get_mountpoint',
                mock.Mock(return_value='/cgroups'))
    @mock.patch('os.makedirs', mock.Mock())
    def test_create(self):
        """Tests cgroup creation."""
        group = os.path.join('treadmill', 'apps', 'test1')
        cgroups.create('cpu', group)
        cgroups.create('memory', group)
        cgroups.create('cpuacct', group)
        os.makedirs.assert_has_calls(
            [mock.call('/cgroups/treadmill/apps/test1'),
             mock.call('/cgroups/treadmill/apps/test1'),
             mock.call('/cgroups/treadmill/apps/test1')])

    @mock.patch('treadmill.cgroups.get_mountpoint', mock.Mock())
    def test_extractpath(self):
        """ test cgroup name from a cgroup path"""
        treadmill.cgroups.get_mountpoint.return_value = '/fs/cgroup/memory'
        cgrp = cgroups.extractpath('/fs/cgroup/memory/treadmill/core',
                                   'memory')
        self.assertEqual(cgrp, 'treadmill/core')

        cgrp = cgroups.extractpath('/fs/cgroup/memory/treadmill/core/foo',
                                   'memory', 'foo')
        self.assertEqual(cgrp, 'treadmill/core')

        with self.assertRaises(ValueError):
            cgroups.extractpath('/cgroup/memory/treadmill/core', 'memory')

        with self.assertRaises(ValueError):
            cgroups.extractpath('/fs/cgroup/memory/treadmill/core/foo',
                                'cpu', 'bar')

    @mock.patch('treadmill.cgroups.get_mountpoint', mock.Mock())
    @mock.patch('os.rmdir', mock.Mock())
    def test_delete(self):
        """Tests cgroup deletion."""
        cgroups_dir = os.path.join(self.root, 'cgroups')
        treadmill.cgroups.get_mountpoint.return_value = cgroups_dir

        group = os.path.join('treadmill', 'apps', 'test1')
        # Create a directory for the cgroup
        os.makedirs(os.path.join(cgroups_dir, group))

        cgroups.delete('cpu', group)

        os.rmdir.assert_called_once_with(
            os.path.join(cgroups_dir, group)
        )

    @mock.patch('treadmill.cgroups.get_mountpoint',
                mock.Mock(return_value='/cgroups'))
    @mock.patch('io.open', mock.mock_open())
    def test_join(self):
        """Tests joining the cgroup."""
        group = os.path.join('treadmill', 'apps', 'test1')

        cgroups.join('cpu', group, '1234')

        io.open.assert_called_once_with(
            '/cgroups/treadmill/apps/test1/tasks', 'w')
        io.open().write.assert_called_once_with('1234')

    @mock.patch('treadmill.cgroups.mounted_subsystems',
                mock.Mock(return_value={'cpu': '/cgroup/cpu'}))