Skip to content

Commit

Permalink
Adds json schema methods to Option and Config
Browse files Browse the repository at this point in the history
Config schema methods generate a json schemas based on
the config instance's options. Option schema methods
generate schemas based on the type of Option, its
default values, and whether its required or not.

Also adds schema unit tests for Config and Options,
and adds test_float unit test file.

Needed for Freeseer#632
  • Loading branch information
FranciscoCanas committed Nov 20, 2014
1 parent efe8c03 commit 2a128fc
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 8 deletions.
29 changes: 28 additions & 1 deletion src/freeseer/framework/config/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# freeseer - vga/presentation capture software
#
# Copyright (C) 2011, 2013 Free and Open Source Software Learning Centre
# Copyright (C) 2011, 2013, 2014 Free and Open Source Software Learning Centre
# http://fosslc.org
#
# This program is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -56,6 +56,13 @@ def presentation(self, value):
"""Returns a modified version of value that will not itself be persisted."""
return value

def schema(self):
"""Returns the json schema for an Option."""
schema = {'type': self.SCHEMA_TYPE}
if self.default != self.NotSpecified:
schema['default'] = self.default
return schema

# Override these!

@abc.abstractmethod
Expand Down Expand Up @@ -193,6 +200,26 @@ def save(self):
else:
raise StorageNotSetError()

@classmethod
def schema(cls):
"""Returns the json schema for this Config instance."""
required = []

schema = {
'type': 'object',
'properties': {},
}

for name, instance in cls.options.iteritems():
schema['properties'][name] = instance.schema()
if instance.is_required():
required.append(name)

if required:
schema['required'] = required

return schema


class ConfigStorage(object):
"""Defines an interface for loading and storing Config instances."""
Expand Down
14 changes: 13 additions & 1 deletion src/freeseer/framework/config/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# freeseer - vga/presentation capture software
#
# Copyright (C) 2011, 2013 Free and Open Source Software Learning Centre
# Copyright (C) 2011, 2013, 2014 Free and Open Source Software Learning Centre
# http://fosslc.org
#
# This program is free software: you can redistribute it and/or modify
Expand All @@ -30,6 +30,7 @@

class StringOption(Option):
"""Represents a string value."""
SCHEMA_TYPE = 'string'

def is_valid(self, value):
return isinstance(value, str) or hasattr(value, '__str__')
Expand All @@ -43,6 +44,7 @@ def decode(self, value):

class IntegerOption(Option):
"""Represents an integer number value."""
SCHEMA_TYPE = 'integer'

def is_valid(self, value):
return isinstance(value, int)
Expand All @@ -59,6 +61,7 @@ def decode(self, value):

class FloatOption(Option):
"""Represents a floating point number value."""
SCHEMA_TYPE = 'number'

def is_valid(self, value):
return isinstance(value, float)
Expand All @@ -75,6 +78,7 @@ def decode(self, value):

class BooleanOption(Option):
"""Represents a boolean value."""
SCHEMA_TYPE = 'boolean'

def is_valid(self, value):
return isinstance(value, bool)
Expand All @@ -88,6 +92,7 @@ def decode(self, value):

class FolderOption(Option):
"""Represents the path to a folder."""
SCHEMA_TYPE = 'string'

def __init__(self, default=Option.NotSpecified, auto_create=False):
self.auto_create = auto_create
Expand Down Expand Up @@ -119,6 +124,7 @@ def presentation(self, value):

class ChoiceOption(StringOption):
"""Represents a selection from a pre-defined list of strings."""
SCHEMA_TYPE = 'enum'

def __init__(self, choices, default=Option.NotSpecified):
self.choices = choices
Expand All @@ -133,3 +139,9 @@ def decode(self, value):
return choice
else:
raise InvalidDecodeValueError(value)

def schema(self):
schema = {'enum': self.choices}
if self.default != Option.NotSpecified:
schema['default'] = self.default
return schema
17 changes: 16 additions & 1 deletion src/freeseer/tests/framework/config/options/test_boolean.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# freeseer - vga/presentation capture software
#
# Copyright (C) 2011, 2013 Free and Open Source Software Learning Centre
# Copyright (C) 2011, 2013, 2014 Free and Open Source Software Learning Centre
# http://fosslc.org
#
# This program is free software: you can redistribute it and/or modify
Expand All @@ -24,6 +24,9 @@

import unittest

from jsonschema import validate
from jsonschema import ValidationError

from freeseer.framework.config.options import BooleanOption
from freeseer.tests.framework.config.options import OptionTest

Expand Down Expand Up @@ -53,6 +56,12 @@ class TestBooleanOptionNoDefault(unittest.TestCase, OptionTest):
def setUp(self):
self.option = BooleanOption()

def test_schema(self):
"""Tests BooleanOption schema method."""
self.assertRaises(ValidationError, validate, 4, self.option.schema())
self.assertIsNone(validate(True, self.option.schema()))
self.assertDictEqual(self.option.schema(), {'type': 'boolean'})


class TestBooleanOptionWithDefault(TestBooleanOptionNoDefault):
"""Test BooleanOption with a default value."""
Expand All @@ -63,3 +72,9 @@ def setUp(self):
def test_default(self):
"""Tests that the default was set correctly."""
self.assertEqual(self.option.default, False)

def test_schema(self):
"""Tests BooleanOption schema method."""
self.assertRaises(ValidationError, validate, 4, self.option.schema())
self.assertIsNone(validate(True, self.option.schema()))
self.assertDictEqual(self.option.schema(), {'default': False, 'type': 'boolean'})
30 changes: 29 additions & 1 deletion src/freeseer/tests/framework/config/options/test_choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# freeseer - vga/presentation capture software
#
# Copyright (C) 2011, 2013 Free and Open Source Software Learning Centre
# Copyright (C) 2011, 2013, 2014 Free and Open Source Software Learning Centre
# http://fosslc.org
#
# This program is free software: you can redistribute it and/or modify
Expand All @@ -24,6 +24,9 @@

import unittest

from jsonschema import validate
from jsonschema import ValidationError

from freeseer.framework.config.options import ChoiceOption
from freeseer.tests.framework.config.options import OptionTest

Expand Down Expand Up @@ -52,6 +55,18 @@ def setUp(self):
'world',
])

def test_schema(self):
"""Tests a ChoiceOption schema method."""
expected = {
'enum': [
'hello',
'world',
],
}
self.assertRaises(ValidationError, validate, 'error', self.option.schema())
self.assertIsNone(validate('world', self.option.schema()))
self.assertDictEqual(self.option.schema(), expected)


class TestChoiceOptionWithDefault(TestChoiceOptionNoDefault):
"""Tests ChoiceOption with a default value."""
Expand All @@ -65,3 +80,16 @@ def setUp(self):
def test_default(self):
"""Tests that the default was set correctly."""
self.assertEqual(self.option.default, 'hello')

def test_schema(self):
"""Tests a ChoiceOption schema method."""
expected = {
'default': 'hello',
'enum': [
'hello',
'world',
],
}
self.assertRaises(ValidationError, validate, 'error', self.option.schema())
self.assertIsNone(validate('world', self.option.schema()))
self.assertDictEqual(self.option.schema(), expected)
72 changes: 72 additions & 0 deletions src/freeseer/tests/framework/config/options/test_float.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# freeseer - vga/presentation capture software
#
# Copyright (C) 2014 Free and Open Source Software Learning Centre
# http://fosslc.org
#
# 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 3 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, see <http://www.gnu.org/licenses/>.

# For support, questions, suggestions or any other inquiries, visit:
# http://wiki.github.com/Freeseer/freeseer/

import unittest

from jsonschema import validate
from jsonschema import ValidationError

from freeseer.framework.config.options import FloatOption
from freeseer.tests.framework.config.options import OptionTest


class TestFloatOptionNoDefault(unittest.TestCase, OptionTest):
"""Tests FloatOption without a default value."""

valid_success = [x / 10.0 for x in xrange(-100, 100)]

encode_success = zip(valid_success, map(str, valid_success))

decode_success = zip(map(str, valid_success), valid_success)
decode_failure = [
'hello',
'1world',
'test2',
]

def setUp(self):
self.option = FloatOption()

def test_schema(self):
"""Tests FloatOption schema method."""
self.assertRaises(ValidationError, validate, 'error', self.option.schema())
self.assertIsNone(validate(5.5, self.option.schema()))
self.assertDictEqual(self.option.schema(), {'type': 'number'})


class TestFloatOptionWithDefault(TestFloatOptionNoDefault):
"""Tests FloatOption with a default value."""

def setUp(self):
self.option = FloatOption(1234.5)

def test_default(self):
"""Tests that the default was set correctly."""
self.assertEqual(self.option.default, 1234.5)

def test_schema(self):
"""Tests FloatOption schema method."""
self.assertRaises(ValidationError, validate, 'error', self.option.schema())
self.assertIsNone(validate(5.0, self.option.schema()))
self.assertDictEqual(self.option.schema(), {'default': 1234.5, 'type': 'number'})
17 changes: 16 additions & 1 deletion src/freeseer/tests/framework/config/options/test_folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# freeseer - vga/presentation capture software
#
# Copyright (C) 2011, 2013 Free and Open Source Software Learning Centre
# Copyright (C) 2011, 2013, 2014 Free and Open Source Software Learning Centre
# http://fosslc.org
#
# This program is free software: you can redistribute it and/or modify
Expand All @@ -27,6 +27,9 @@
import tempfile
import unittest

from jsonschema import validate
from jsonschema import ValidationError

from freeseer.framework.config.options import FolderOption
from freeseer.tests.framework.config.options import OptionTest

Expand Down Expand Up @@ -57,6 +60,12 @@ def test_presentation(self):
self.assertEqual(presentation_value, path)
self.assertFalse(os.path.exists(presentation_value))

def test_schema(self):
"""Tests StringOption schema method."""
self.assertRaises(ValidationError, validate, 1, self.option.schema())
self.assertIsNone(validate('/tmp2', self.option.schema()))
self.assertDictEqual(self.option.schema(), {'type': 'string'})


class TestFolderOptionAutoCreate(TestFolderOptionNoDefault):
"""Tests FolderOption without a default value, and with auto_create turned on."""
Expand Down Expand Up @@ -88,3 +97,9 @@ def setUp(self):
def test_default(self):
"""Tests that the default was set correctly."""
self.assertEqual(self.option.default, '/tmp')

def test_schema(self):
"""Tests StringOption schema method."""
self.assertRaises(ValidationError, validate, 1, self.option.schema())
self.assertIsNone(validate('/tmp2', self.option.schema()))
self.assertDictEqual(self.option.schema(), {'default': '/tmp', 'type': 'string'})
17 changes: 16 additions & 1 deletion src/freeseer/tests/framework/config/options/test_integer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# freeseer - vga/presentation capture software
#
# Copyright (C) 2011, 2013 Free and Open Source Software Learning Centre
# Copyright (C) 2011, 2013, 2014 Free and Open Source Software Learning Centre
# http://fosslc.org
#
# This program is free software: you can redistribute it and/or modify
Expand All @@ -24,6 +24,9 @@

import unittest

from jsonschema import validate
from jsonschema import ValidationError

from freeseer.framework.config.options import IntegerOption
from freeseer.tests.framework.config.options import OptionTest

Expand All @@ -45,6 +48,12 @@ class TestIntegerOptionNoDefault(unittest.TestCase, OptionTest):
def setUp(self):
self.option = IntegerOption()

def test_schema(self):
"""Tests IntegerOption schema method."""
self.assertRaises(ValidationError, validate, 1.0, self.option.schema())
self.assertIsNone(validate(5, self.option.schema()))
self.assertDictEqual(self.option.schema(), {'type': 'integer'})


class TestIntegerOptionWithDefault(TestIntegerOptionNoDefault):
"""Tests IntegerOption with a default value."""
Expand All @@ -55,3 +64,9 @@ def setUp(self):
def test_default(self):
"""Tests that the default was set correctly."""
self.assertEqual(self.option.default, 1234)

def test_schema(self):
"""Tests IntegerOption schema method."""
self.assertRaises(ValidationError, validate, 1.0, self.option.schema())
self.assertIsNone(validate(5, self.option.schema()))
self.assertDictEqual(self.option.schema(), {'default': 1234, 'type': 'integer'})
Loading

0 comments on commit 2a128fc

Please sign in to comment.