Skip to content

Commit 2a128fc

Browse files
Adds json schema methods to Option and Config
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
1 parent efe8c03 commit 2a128fc

File tree

9 files changed

+287
-8
lines changed

9 files changed

+287
-8
lines changed

src/freeseer/framework/config/core.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

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

59+
def schema(self):
60+
"""Returns the json schema for an Option."""
61+
schema = {'type': self.SCHEMA_TYPE}
62+
if self.default != self.NotSpecified:
63+
schema['default'] = self.default
64+
return schema
65+
5966
# Override these!
6067

6168
@abc.abstractmethod
@@ -193,6 +200,26 @@ def save(self):
193200
else:
194201
raise StorageNotSetError()
195202

203+
@classmethod
204+
def schema(cls):
205+
"""Returns the json schema for this Config instance."""
206+
required = []
207+
208+
schema = {
209+
'type': 'object',
210+
'properties': {},
211+
}
212+
213+
for name, instance in cls.options.iteritems():
214+
schema['properties'][name] = instance.schema()
215+
if instance.is_required():
216+
required.append(name)
217+
218+
if required:
219+
schema['required'] = required
220+
221+
return schema
222+
196223

197224
class ConfigStorage(object):
198225
"""Defines an interface for loading and storing Config instances."""

src/freeseer/framework/config/options.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

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

3131
class StringOption(Option):
3232
"""Represents a string value."""
33+
SCHEMA_TYPE = 'string'
3334

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

4445
class IntegerOption(Option):
4546
"""Represents an integer number value."""
47+
SCHEMA_TYPE = 'integer'
4648

4749
def is_valid(self, value):
4850
return isinstance(value, int)
@@ -59,6 +61,7 @@ def decode(self, value):
5961

6062
class FloatOption(Option):
6163
"""Represents a floating point number value."""
64+
SCHEMA_TYPE = 'number'
6265

6366
def is_valid(self, value):
6467
return isinstance(value, float)
@@ -75,6 +78,7 @@ def decode(self, value):
7578

7679
class BooleanOption(Option):
7780
"""Represents a boolean value."""
81+
SCHEMA_TYPE = 'boolean'
7882

7983
def is_valid(self, value):
8084
return isinstance(value, bool)
@@ -88,6 +92,7 @@ def decode(self, value):
8892

8993
class FolderOption(Option):
9094
"""Represents the path to a folder."""
95+
SCHEMA_TYPE = 'string'
9196

9297
def __init__(self, default=Option.NotSpecified, auto_create=False):
9398
self.auto_create = auto_create
@@ -119,6 +124,7 @@ def presentation(self, value):
119124

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

123129
def __init__(self, choices, default=Option.NotSpecified):
124130
self.choices = choices
@@ -133,3 +139,9 @@ def decode(self, value):
133139
return choice
134140
else:
135141
raise InvalidDecodeValueError(value)
142+
143+
def schema(self):
144+
schema = {'enum': self.choices}
145+
if self.default != Option.NotSpecified:
146+
schema['default'] = self.default
147+
return schema

src/freeseer/tests/framework/config/options/test_boolean.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

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

2525
import unittest
2626

27+
from jsonschema import validate
28+
from jsonschema import ValidationError
29+
2730
from freeseer.framework.config.options import BooleanOption
2831
from freeseer.tests.framework.config.options import OptionTest
2932

@@ -53,6 +56,12 @@ class TestBooleanOptionNoDefault(unittest.TestCase, OptionTest):
5356
def setUp(self):
5457
self.option = BooleanOption()
5558

59+
def test_schema(self):
60+
"""Tests BooleanOption schema method."""
61+
self.assertRaises(ValidationError, validate, 4, self.option.schema())
62+
self.assertIsNone(validate(True, self.option.schema()))
63+
self.assertDictEqual(self.option.schema(), {'type': 'boolean'})
64+
5665

5766
class TestBooleanOptionWithDefault(TestBooleanOptionNoDefault):
5867
"""Test BooleanOption with a default value."""
@@ -63,3 +72,9 @@ def setUp(self):
6372
def test_default(self):
6473
"""Tests that the default was set correctly."""
6574
self.assertEqual(self.option.default, False)
75+
76+
def test_schema(self):
77+
"""Tests BooleanOption schema method."""
78+
self.assertRaises(ValidationError, validate, 4, self.option.schema())
79+
self.assertIsNone(validate(True, self.option.schema()))
80+
self.assertDictEqual(self.option.schema(), {'default': False, 'type': 'boolean'})

src/freeseer/tests/framework/config/options/test_choice.py

+29-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

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

2525
import unittest
2626

27+
from jsonschema import validate
28+
from jsonschema import ValidationError
29+
2730
from freeseer.framework.config.options import ChoiceOption
2831
from freeseer.tests.framework.config.options import OptionTest
2932

@@ -52,6 +55,18 @@ def setUp(self):
5255
'world',
5356
])
5457

58+
def test_schema(self):
59+
"""Tests a ChoiceOption schema method."""
60+
expected = {
61+
'enum': [
62+
'hello',
63+
'world',
64+
],
65+
}
66+
self.assertRaises(ValidationError, validate, 'error', self.option.schema())
67+
self.assertIsNone(validate('world', self.option.schema()))
68+
self.assertDictEqual(self.option.schema(), expected)
69+
5570

5671
class TestChoiceOptionWithDefault(TestChoiceOptionNoDefault):
5772
"""Tests ChoiceOption with a default value."""
@@ -65,3 +80,16 @@ def setUp(self):
6580
def test_default(self):
6681
"""Tests that the default was set correctly."""
6782
self.assertEqual(self.option.default, 'hello')
83+
84+
def test_schema(self):
85+
"""Tests a ChoiceOption schema method."""
86+
expected = {
87+
'default': 'hello',
88+
'enum': [
89+
'hello',
90+
'world',
91+
],
92+
}
93+
self.assertRaises(ValidationError, validate, 'error', self.option.schema())
94+
self.assertIsNone(validate('world', self.option.schema()))
95+
self.assertDictEqual(self.option.schema(), expected)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
# freeseer - vga/presentation capture software
5+
#
6+
# Copyright (C) 2014 Free and Open Source Software Learning Centre
7+
# http://fosslc.org
8+
#
9+
# This program is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License as published by
11+
# the Free Software Foundation, either version 3 of the License, or
12+
# (at your option) any later version.
13+
#
14+
# This program is distributed in the hope that it will be useful,
15+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
# GNU General Public License for more details.
18+
#
19+
# You should have received a copy of the GNU General Public License
20+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
22+
# For support, questions, suggestions or any other inquiries, visit:
23+
# http://wiki.github.com/Freeseer/freeseer/
24+
25+
import unittest
26+
27+
from jsonschema import validate
28+
from jsonschema import ValidationError
29+
30+
from freeseer.framework.config.options import FloatOption
31+
from freeseer.tests.framework.config.options import OptionTest
32+
33+
34+
class TestFloatOptionNoDefault(unittest.TestCase, OptionTest):
35+
"""Tests FloatOption without a default value."""
36+
37+
valid_success = [x / 10.0 for x in xrange(-100, 100)]
38+
39+
encode_success = zip(valid_success, map(str, valid_success))
40+
41+
decode_success = zip(map(str, valid_success), valid_success)
42+
decode_failure = [
43+
'hello',
44+
'1world',
45+
'test2',
46+
]
47+
48+
def setUp(self):
49+
self.option = FloatOption()
50+
51+
def test_schema(self):
52+
"""Tests FloatOption schema method."""
53+
self.assertRaises(ValidationError, validate, 'error', self.option.schema())
54+
self.assertIsNone(validate(5.5, self.option.schema()))
55+
self.assertDictEqual(self.option.schema(), {'type': 'number'})
56+
57+
58+
class TestFloatOptionWithDefault(TestFloatOptionNoDefault):
59+
"""Tests FloatOption with a default value."""
60+
61+
def setUp(self):
62+
self.option = FloatOption(1234.5)
63+
64+
def test_default(self):
65+
"""Tests that the default was set correctly."""
66+
self.assertEqual(self.option.default, 1234.5)
67+
68+
def test_schema(self):
69+
"""Tests FloatOption schema method."""
70+
self.assertRaises(ValidationError, validate, 'error', self.option.schema())
71+
self.assertIsNone(validate(5.0, self.option.schema()))
72+
self.assertDictEqual(self.option.schema(), {'default': 1234.5, 'type': 'number'})

src/freeseer/tests/framework/config/options/test_folder.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

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

30+
from jsonschema import validate
31+
from jsonschema import ValidationError
32+
3033
from freeseer.framework.config.options import FolderOption
3134
from freeseer.tests.framework.config.options import OptionTest
3235

@@ -57,6 +60,12 @@ def test_presentation(self):
5760
self.assertEqual(presentation_value, path)
5861
self.assertFalse(os.path.exists(presentation_value))
5962

63+
def test_schema(self):
64+
"""Tests StringOption schema method."""
65+
self.assertRaises(ValidationError, validate, 1, self.option.schema())
66+
self.assertIsNone(validate('/tmp2', self.option.schema()))
67+
self.assertDictEqual(self.option.schema(), {'type': 'string'})
68+
6069

6170
class TestFolderOptionAutoCreate(TestFolderOptionNoDefault):
6271
"""Tests FolderOption without a default value, and with auto_create turned on."""
@@ -88,3 +97,9 @@ def setUp(self):
8897
def test_default(self):
8998
"""Tests that the default was set correctly."""
9099
self.assertEqual(self.option.default, '/tmp')
100+
101+
def test_schema(self):
102+
"""Tests StringOption schema method."""
103+
self.assertRaises(ValidationError, validate, 1, self.option.schema())
104+
self.assertIsNone(validate('/tmp2', self.option.schema()))
105+
self.assertDictEqual(self.option.schema(), {'default': '/tmp', 'type': 'string'})

src/freeseer/tests/framework/config/options/test_integer.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

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

2525
import unittest
2626

27+
from jsonschema import validate
28+
from jsonschema import ValidationError
29+
2730
from freeseer.framework.config.options import IntegerOption
2831
from freeseer.tests.framework.config.options import OptionTest
2932

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

51+
def test_schema(self):
52+
"""Tests IntegerOption schema method."""
53+
self.assertRaises(ValidationError, validate, 1.0, self.option.schema())
54+
self.assertIsNone(validate(5, self.option.schema()))
55+
self.assertDictEqual(self.option.schema(), {'type': 'integer'})
56+
4857

4958
class TestIntegerOptionWithDefault(TestIntegerOptionNoDefault):
5059
"""Tests IntegerOption with a default value."""
@@ -55,3 +64,9 @@ def setUp(self):
5564
def test_default(self):
5665
"""Tests that the default was set correctly."""
5766
self.assertEqual(self.option.default, 1234)
67+
68+
def test_schema(self):
69+
"""Tests IntegerOption schema method."""
70+
self.assertRaises(ValidationError, validate, 1.0, self.option.schema())
71+
self.assertIsNone(validate(5, self.option.schema()))
72+
self.assertDictEqual(self.option.schema(), {'default': 1234, 'type': 'integer'})

0 commit comments

Comments
 (0)