diff --git a/qubes/Makefile b/qubes/Makefile index 0c0f9563..59f48a1f 100644 --- a/qubes/Makefile +++ b/qubes/Makefile @@ -75,5 +75,6 @@ endif cp \ tests/tools/__init__.py* \ + tests/tools/init.py* \ tests/tools/qvm_ls.py* \ $(DESTDIR)$(PYTHON_QUBESPATH)/tests/tools diff --git a/qubes/tests/tools/init.py b/qubes/tests/tools/init.py new file mode 100644 index 00000000..f6e103ab --- /dev/null +++ b/qubes/tests/tools/init.py @@ -0,0 +1,142 @@ +#!/usr/bin/python2 -O +# vim: fileencoding=utf-8 + +# +# The Qubes OS Project, https://www.qubes-os.org/ +# +# Copyright (C) 2015 Joanna Rutkowska +# Copyright (C) 2015 Wojtek Porczyk +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import argparse + +import qubes +import qubes.tools + +import qubes.tests + +class TC_00_PropertyAction(qubes.tests.QubesTestCase): + def test_000_default(self): + parser = argparse.ArgumentParser() + parser.add_argument('--property', '-p', + action=qubes.tools.PropertyAction) + parser.set_defaults(properties={'defaultprop': 'defaultvalue'}) + + args = parser.parse_args([]) + self.assertDictContainsSubset( + {'defaultprop': 'defaultvalue'}, args.properties) + + def test_001_set_prop(self): + parser = argparse.ArgumentParser() + parser.add_argument('--property', '-p', + action=qubes.tools.PropertyAction) + + args = parser.parse_args(['-p', 'testprop=testvalue']) + self.assertDictContainsSubset( + {'testprop': 'testvalue'}, args.properties) + + def test_002_set_prop_2(self): + parser = argparse.ArgumentParser() + parser.add_argument('--property', '-p', + action=qubes.tools.PropertyAction) + parser.set_defaults(properties={'defaultprop': 'defaultvalue'}) + + args = parser.parse_args( + ['-p', 'testprop=testvalue', '-p', 'testprop2=testvalue2']) + self.assertDictContainsSubset( + {'testprop': 'testvalue', 'testprop2': 'testvalue2'}, + args.properties) + + def test_003_set_prop_with_default(self): + parser = argparse.ArgumentParser() + parser.add_argument('--property', '-p', + action=qubes.tools.PropertyAction) + parser.set_defaults(properties={'defaultprop': 'defaultvalue'}) + + args = parser.parse_args(['-p', 'testprop=testvalue']) + self.assertDictContainsSubset( + {'testprop': 'testvalue', 'defaultprop': 'defaultvalue'}, + args.properties) + + def test_003_set_prop_override_default(self): + parser = argparse.ArgumentParser() + parser.add_argument('--property', '-p', + action=qubes.tools.PropertyAction) + parser.set_defaults(properties={'testprop': 'defaultvalue'}) + + args = parser.parse_args(['-p', 'testprop=testvalue']) + self.assertDictContainsSubset( + {'testprop': 'testvalue'}, + args.properties) + + +class TC_01_SinglePropertyAction(qubes.tests.QubesTestCase): + def test_000_help(self): + parser = argparse.ArgumentParser() + action = parser.add_argument('--testprop', '-T', + action=qubes.tools.SinglePropertyAction) + self.assertIn('testprop', action.help) + + def test_001_help_const(self): + parser = argparse.ArgumentParser() + action = parser.add_argument('--testprop', '-T', + action=qubes.tools.SinglePropertyAction, + const='testvalue') + self.assertIn('testvalue', action.help) + + def test_100_default(self): + parser = argparse.ArgumentParser() + parser.add_argument('--testprop', '-T', + action=qubes.tools.SinglePropertyAction) + parser.set_defaults(properties={'testprop': 'defaultvalue'}) + + args = parser.parse_args([]) + self.assertDictContainsSubset( + {'testprop': 'defaultvalue'}, args.properties) + + def test_101_set_prop(self): + parser = argparse.ArgumentParser() + parser.add_argument('--testprop', '-T', + action=qubes.tools.SinglePropertyAction) + args = parser.parse_args(['-T', 'testvalue']) + self.assertDictContainsSubset( + {'testprop': 'testvalue'}, args.properties) + + def test_102_set_prop_dest(self): + parser = argparse.ArgumentParser() + parser.add_argument('--testprop', '-T', dest='otherprop', + action=qubes.tools.SinglePropertyAction) + args = parser.parse_args(['-T', 'testvalue']) + self.assertDictContainsSubset( + {'otherprop': 'testvalue'}, args.properties) + + def test_103_set_prop_const(self): + parser = argparse.ArgumentParser() + parser.add_argument('--testprop', '-T', + action=qubes.tools.SinglePropertyAction, + const='testvalue') + args = parser.parse_args(['-T']) + self.assertDictContainsSubset( + {'testprop': 'testvalue'}, args.properties) + + def test_104_set_prop_positional(self): + parser = argparse.ArgumentParser() + parser.add_argument('testprop', + action=qubes.tools.SinglePropertyAction) + args = parser.parse_args(['testvalue']) + self.assertDictContainsSubset( + {'testprop': 'testvalue'}, args.properties) diff --git a/qubes/tools/__init__.py b/qubes/tools/__init__.py index 4d51bc9a..98517757 100644 --- a/qubes/tools/__init__.py +++ b/qubes/tools/__init__.py @@ -30,6 +30,59 @@ import importlib import os +class PropertyAction(argparse.Action): + '''Action for argument parser that stores a property.''' + # pylint: disable=redefined-builtin + def __init__(self, + option_strings, + dest, + metavar='NAME=VALUE', + required=False, + help='set property to a value'): + super(PropertyAction, self).__init__(option_strings, 'properties', + metavar=metavar, default={}, help=help) + + def __call__(self, parser, namespace, values, option_string=None): + try: + prop, value = values.split('=', 1) + except ValueError: + parser.error('invalid property token: {!r}'.format(token)) + + getattr(namespace, self.dest)[prop] = value + + +class SinglePropertyAction(argparse.Action): + '''Action for argument parser that stores a property.''' + + # pylint: disable=redefined-builtin + def __init__(self, + option_strings, + dest, + metavar='VALUE', + const=None, + nargs=None, + required=False, + help=None): + if help is None: + help = 'set {!r} property to a value'.format(dest) + if const is not None: + help += ' {!r}'.format(const) + + if const is not None: + nargs = 0 + + super(SinglePropertyAction, self).__init__(option_strings, 'properties', + metavar=metavar, help=help, default={}, const=const, + nargs=nargs) + + self.name = dest + + + def __call__(self, parser, namespace, values, option_string=None): + getattr(namespace, self.dest)[self.name] = values \ + if self.const is None else self.const + + # TODO --verbose, logger setup def get_parser_base(want_force_root=False, **kwargs): '''Get base parser with options common to all Qubes OS tools. diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index b668f2f5..69f96c27 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -237,6 +237,7 @@ fi %dir %{python_sitearch}/qubes/tests/tools %{python_sitearch}/qubes/tests/tools/__init__.py* +%{python_sitearch}/qubes/tests/tools/init.py* %{python_sitearch}/qubes/tests/tools/qvm_ls.py* # qmemman