blob: 2e2564a6e91b52fbe301bdcf30ffeb01bd4e7367 [file] [log] [blame]
# Copyright 2010-2015 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
"""Tests for the portage.util._xattr module"""
from __future__ import print_function
try:
# Try python-3.3 module first.
# pylint: disable=no-name-in-module
from unittest import mock
except ImportError:
try:
# Try standalone module.
import mock
except ImportError:
mock = None
import subprocess
import portage
from portage.tests import TestCase
from portage.util._xattr import (xattr as _xattr, _XattrSystemCommands,
_XattrStub)
orig_popen = subprocess.Popen
def MockSubprocessPopen(stdin):
"""Helper to mock (closely) a subprocess.Popen call
The module has minor tweaks in behavior when it comes to encoding and
python versions, so use a real subprocess.Popen call to fake out the
runtime behavior. This way we don't have to also implement different
encodings as that gets ugly real fast.
"""
# pylint: disable=protected-access
proc = orig_popen(['cat'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
proc.stdin.write(portage._unicode_encode(stdin, portage._encodings['stdio']))
return proc
class SystemCommandsTest(TestCase):
"""Test _XattrSystemCommands"""
OUTPUT = '\n'.join((
'# file: /bin/ping',
'security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA=',
'user.foo="asdf"',
'',
))
def _setUp(self):
if mock is None:
self.skipTest('need mock for testing')
return _XattrSystemCommands
def _testGetBasic(self):
"""Verify the get() behavior"""
xattr = self._setUp()
with mock.patch.object(subprocess, 'Popen') as call_mock:
# Verify basic behavior, and namespace arg works as expected.
xattr.get('/some/file', 'user.foo')
xattr.get('/some/file', 'foo', namespace='user')
self.assertEqual(call_mock.call_args_list[0], call_mock.call_args_list[1])
# Verify nofollow behavior.
call_mock.reset()
xattr.get('/some/file', 'user.foo', nofollow=True)
self.assertIn('-h', call_mock.call_args[0][0])
def testGetParsing(self):
"""Verify get() parses output sanely"""
xattr = self._setUp()
with mock.patch.object(subprocess, 'Popen') as call_mock:
# Verify output parsing.
call_mock.return_value = MockSubprocessPopen('\n'.join([
'# file: /some/file',
'user.foo="asdf"',
'',
]))
call_mock.reset()
self.assertEqual(xattr.get('/some/file', 'user.foo'), b'"asdf"')
def testGetAllBasic(self):
"""Verify the get_all() behavior"""
xattr = self._setUp()
with mock.patch.object(subprocess, 'Popen') as call_mock:
# Verify basic behavior.
xattr.get_all('/some/file')
# Verify nofollow behavior.
call_mock.reset()
xattr.get_all('/some/file', nofollow=True)
self.assertIn('-h', call_mock.call_args[0][0])
def testGetAllParsing(self):
"""Verify get_all() parses output sanely"""
xattr = self._setUp()
with mock.patch.object(subprocess, 'Popen') as call_mock:
# Verify output parsing.
call_mock.return_value = MockSubprocessPopen(self.OUTPUT)
exp = [
(b'security.capability', b'0sAQAAAgAgAAAAAAAAAAAAAAAAAAA='),
(b'user.foo', b'"asdf"'),
]
self.assertEqual(exp, xattr.get_all('/some/file'))
def testSetBasic(self):
"""Verify the set() behavior"""
xattr = self._setUp()
with mock.patch.object(subprocess, 'Popen') as call_mock:
# Verify basic behavior, and namespace arg works as expected.
xattr.set('/some/file', 'user.foo', 'bar')
xattr.set('/some/file', 'foo', 'bar', namespace='user')
self.assertEqual(call_mock.call_args_list[0], call_mock.call_args_list[1])
def testListBasic(self):
"""Verify the list() behavior"""
xattr = self._setUp()
with mock.patch.object(subprocess, 'Popen') as call_mock:
# Verify basic behavior.
xattr.list('/some/file')
# Verify nofollow behavior.
call_mock.reset()
xattr.list('/some/file', nofollow=True)
self.assertIn('-h', call_mock.call_args[0][0])
def testListParsing(self):
"""Verify list() parses output sanely"""
xattr = self._setUp()
with mock.patch.object(subprocess, 'Popen') as call_mock:
# Verify output parsing.
call_mock.return_value = MockSubprocessPopen(self.OUTPUT)
exp = [b'security.capability', b'user.foo']
self.assertEqual(exp, xattr.list('/some/file'))
def testRemoveBasic(self):
"""Verify the remove() behavior"""
xattr = self._setUp()
with mock.patch.object(subprocess, 'Popen') as call_mock:
# Verify basic behavior, and namespace arg works as expected.
xattr.remove('/some/file', 'user.foo')
xattr.remove('/some/file', 'foo', namespace='user')
self.assertEqual(call_mock.call_args_list[0], call_mock.call_args_list[1])
# Verify nofollow behavior.
call_mock.reset()
xattr.remove('/some/file', 'user.foo', nofollow=True)
self.assertIn('-h', call_mock.call_args[0][0])
class StubTest(TestCase):
"""Test _XattrStub"""
def testBasic(self):
"""Verify the stub is stubby"""
# Would be nice to verify raised errno is OperationNotSupported.
self.assertRaises(OSError, _XattrStub.get, '/', '')
self.assertRaises(OSError, _XattrStub.set, '/', '', '')
self.assertRaises(OSError, _XattrStub.get_all, '/')
self.assertRaises(OSError, _XattrStub.remove, '/', '')
self.assertRaises(OSError, _XattrStub.list, '/')
class StandardTest(TestCase):
"""Test basic xattr API"""
MODULES = (_xattr, _XattrSystemCommands, _XattrStub)
FUNCS = ('get', 'get_all', 'set', 'remove', 'list')
def testApi(self):
"""Make sure the exported API matches"""
for mod in self.MODULES:
for f in self.FUNCS:
self.assertTrue(hasattr(mod, f),
'%s func missing in %s' % (f, mod))