From 7f7fa2f91467fdb2b11312be8562e7b51fdeb199 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <sebastian.koslowski@gmail.com>
Date: Tue, 3 May 2016 17:13:08 +0200
Subject: grc: added yaml/mako support

Includes basic converter from XML/Cheetah to YAML/Mako based block format.
---
 grc/tests/__init__.py               |   0
 grc/tests/resources/file1.xml       |  58 ++++++++++++++++
 grc/tests/resources/file2.xml       |  80 ++++++++++++++++++++++
 grc/tests/resources/file3.xml       | 100 +++++++++++++++++++++++++++
 grc/tests/test_block_flags.py       |  26 +++++++
 grc/tests/test_block_templates.py   |  45 ++++++++++++
 grc/tests/test_cheetah_converter.py | 132 ++++++++++++++++++++++++++++++++++++
 grc/tests/test_evaled_property.py   | 104 ++++++++++++++++++++++++++++
 grc/tests/test_expr_utils.py        |  41 +++++++++++
 grc/tests/test_generator.py         |  46 +++++++++++++
 grc/tests/test_xml_parser.py        |  39 +++++++++++
 grc/tests/test_yaml_checker.py      |  84 +++++++++++++++++++++++
 12 files changed, 755 insertions(+)
 create mode 100644 grc/tests/__init__.py
 create mode 100644 grc/tests/resources/file1.xml
 create mode 100644 grc/tests/resources/file2.xml
 create mode 100644 grc/tests/resources/file3.xml
 create mode 100644 grc/tests/test_block_flags.py
 create mode 100644 grc/tests/test_block_templates.py
 create mode 100644 grc/tests/test_cheetah_converter.py
 create mode 100644 grc/tests/test_evaled_property.py
 create mode 100644 grc/tests/test_expr_utils.py
 create mode 100644 grc/tests/test_generator.py
 create mode 100644 grc/tests/test_xml_parser.py
 create mode 100644 grc/tests/test_yaml_checker.py

(limited to 'grc/tests')

diff --git a/grc/tests/__init__.py b/grc/tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/grc/tests/resources/file1.xml b/grc/tests/resources/file1.xml
new file mode 100644
index 0000000000..f03288b85d
--- /dev/null
+++ b/grc/tests/resources/file1.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+Copyright 2014 Free Software Foundation, Inc.
+This file is part of GNU Radio
+
+GNU Radio Companion 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.
+
+GNU Radio Companion 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
+-->
+<block>
+    <name>testname</name>
+    <key>block_key</key>
+    <make>blocks.complex_to_mag_squared($(vlen))</make>
+    <param>
+        <name>Vec Length</name>
+        <key>vlen</key>
+        <value>1</value>
+        <type>int</type>
+        <tab>test</tab>
+    </param>
+    <param>
+        <name>Vec Length</name>
+        <key>out_type</key>
+        <value>complex</value>
+        <type>string</type>
+    </param>
+    <param>
+        <name>Alpha</name>
+        <key>a</key>
+        <value>0</value>
+        <type>($out_type)</type>
+    </param>
+    <check>$vlen &gt; 0</check>
+    <sink>
+        <name>in</name>
+        <type>complex</type>
+        <vlen>2 * $vlen</vlen>
+    </sink>
+    <sink>
+        <name>in2</name>
+        <type>message</type>
+    </sink>
+    <source>
+        <name>out</name>
+        <type>$out_type</type>
+        <vlen>$vlen</vlen>
+    </source>
+</block>
diff --git a/grc/tests/resources/file2.xml b/grc/tests/resources/file2.xml
new file mode 100644
index 0000000000..1300c7f5a1
--- /dev/null
+++ b/grc/tests/resources/file2.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!--
+Copyright 2014 Free Software Foundation, Inc.
+This file is part of GNU Radio
+
+GNU Radio Companion 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.
+
+GNU Radio Companion 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
+-->
+<!--
+###################################################
+## And Const Block:
+##	all types, 1 output, 1 input & const
+###################################################
+ -->
+<block>
+	<name>And Const</name>
+	<key>blocks_and_const_xx</key>
+	<category>test</category>
+	<throttle>1</throttle>
+	<import>from gnuradio import blocks</import>
+	<make>blocks.and_const_$(type.fcn)($const)</make>
+	<callback>set_k($const)</callback>
+	<param>
+		<name>IO Type</name>
+		<key>type</key>
+		<type>enum</type>
+		<option>
+			<name>Int</name>
+			<key>int</key>
+			<opt>fcn:ii</opt>
+		</option>
+		<option>
+			<name>Short</name>
+			<key>short</key>
+			<opt>fcn:ss</opt>
+		</option>
+		<option>
+			<name>Byte</name>
+			<key>byte</key>
+			<opt>fcn:bb</opt>
+		</option>
+	</param>
+	<param>
+		<name>Constant</name>
+		<key>const</key>
+		<value>0</value>
+		<type>${type}</type>
+        <hide>#if $log then 'none' else 'part'#</hide>
+	</param>
+	<sink>
+		<name>in</name>
+		<type>$type</type>
+	</sink>
+	<source>
+		<name>out</name>
+		<type>$(type.fcn)</type>
+	</source>
+	<doc>
+This block creates a variable check box. \
+Leave the label blank to use the variable id as the label.
+
+A check box selects between two values of similar type. \
+Te values do not necessarily need to be of boolean type.
+
+The GUI hint can be used to position the widget within the application. \
+The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \
+Both the tab specification and the grid position are optional.
+	</doc>
+</block>
diff --git a/grc/tests/resources/file3.xml b/grc/tests/resources/file3.xml
new file mode 100644
index 0000000000..71753badb1
--- /dev/null
+++ b/grc/tests/resources/file3.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+<!--
+Copyright 2014 Free Software Foundation, Inc.
+This file is part of GNU Radio
+
+GNU Radio Companion 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.
+
+GNU Radio Companion 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
+-->
+<!--
+###################################################
+##Variable Check Box:
+##	a gui check box form
+###################################################
+ -->
+<block>
+	<name>QT GUI Check Box</name>
+	<key>variable_qtgui_check_box</key>
+	<import>from PyQt4 import Qt</import>
+	<var_make>self.$(id) = $(id) = $value</var_make>
+	<make>#set $win = '_%s_check_box'%$id
+#if not $label()
+	#set $label = '"%s"'%$id
+#end if
+$win = Qt.QCheckBox($label)
+self._$(id)_choices = {True: $true, False: $false}
+self._$(id)_choices_inv = dict((v,k) for k,v in self._$(id)_choices.iteritems())
+self._$(id)_callback = lambda i: Qt.QMetaObject.invokeMethod($(win), "setChecked", Qt.Q_ARG("bool", self._$(id)_choices_inv[i]))
+self._$(id)_callback(self.$id)
+$(win).stateChanged.connect(lambda i: self.set_$(id)(self._$(id)_choices[bool(i)]))
+$(gui_hint()($win))</make>
+	<callback>self.set_$(id)($value)</callback>
+	<callback>self._$(id)_callback($id)</callback>
+	<param>
+		<name>Label</name>
+		<key>label</key>
+		<value></value>
+		<type>string</type>
+		<hide>#if $label() then 'none' else 'part'#</hide>
+	</param>
+	<param>
+		<name>Type</name>
+		<key>type</key>
+		<value>int</value>
+		<type>enum</type>
+		<hide>part</hide>
+		<option><name>Float</name><key>real</key><opt>conv:float</opt></option>
+		<option><name>Integer</name><key>int</key><opt>conv:int</opt></option>
+		<option><name>String</name><key>string</key><opt>conv:str</opt></option>
+		<option><name>Boolean</name><key>bool</key><opt>conv:bool</opt></option>
+		<option><name>Any</name><key>raw</key><opt>conv:eval</opt></option>
+	</param>
+	<param>
+		<name>Default Value</name>
+		<key>value</key>
+		<value>True</value>
+		<type>$type</type>
+	</param>
+	<param>
+		<name>True</name>
+		<key>true</key>
+		<value>True</value>
+		<type>$type</type>
+	</param>
+	<param>
+		<name>False</name>
+		<key>false</key>
+		<value>False</value>
+		<type>$type</type>
+	</param>
+	<param>
+		<name>GUI Hint</name>
+		<key>gui_hint</key>
+		<value></value>
+		<type>gui_hint</type>
+		<hide>part</hide>
+	</param>
+	<check>$value in ($true, $false)</check>
+	<doc>
+This block creates a variable check box. \
+Leave the label blank to use the variable id as the label.
+
+A check box selects between two values of similar type. \
+Te values do not necessarily need to be of boolean type.
+
+The GUI hint can be used to position the widget within the application. \
+The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \
+Both the tab specification and the grid position are optional.
+	</doc>
+</block>
diff --git a/grc/tests/test_block_flags.py b/grc/tests/test_block_flags.py
new file mode 100644
index 0000000000..9eecaf20d7
--- /dev/null
+++ b/grc/tests/test_block_flags.py
@@ -0,0 +1,26 @@
+
+from grc.core.blocks._flags import Flags
+
+
+def test_simple():
+    assert 'test' in Flags('_test_')
+
+
+def test_deprecated():
+    assert Flags.DEPRECATED == 'deprecated'
+    assert Flags('this is deprecated').deprecated is True
+
+
+def test_extend():
+    f = Flags('a')
+    f += 'b'
+    assert isinstance(f, Flags)
+    f += u'b'
+    assert isinstance(f, Flags)
+    f = Flags(u'a')
+    f += 'b'
+    assert isinstance(f, Flags)
+    f += u'b'
+    assert isinstance(f, Flags)
+
+    assert str(f) == 'abb'
diff --git a/grc/tests/test_block_templates.py b/grc/tests/test_block_templates.py
new file mode 100644
index 0000000000..df9ab37550
--- /dev/null
+++ b/grc/tests/test_block_templates.py
@@ -0,0 +1,45 @@
+import pytest
+
+from grc.core.blocks._templates import MakoTemplates
+from grc.core.errors import TemplateError
+
+
+class Block(object):
+    namespace_templates = {}
+
+    templates = MakoTemplates(None)
+
+    def __init__(self, **kwargs):
+        self.namespace_templates.update(kwargs)
+
+
+def test_simple():
+    t = MakoTemplates(_bind_to=Block(num='123'), test='abc${num}')
+    assert t['test'] == 'abc${num}'
+    assert t.render('test') == 'abc123'
+    assert 'abc${num}' in t._template_cache
+
+
+def test_instance():
+    block = Block(num='123')
+    block.templates['test'] = 'abc${num}'
+    assert block.templates.render('test') == 'abc123'
+    assert block.templates is block.__dict__['templates']
+
+
+def test_list():
+    templates = ['abc${num}', '${2 * num}c']
+    t = MakoTemplates(_bind_to=Block(num='123'), test=templates)
+    assert t['test'] == templates
+    assert t.render('test') == ['abc123', '123123c']
+    assert set(templates) == set(t._template_cache.keys())
+
+
+def test_parse_error():
+    with pytest.raises(TemplateError):
+        MakoTemplates(_bind_to=Block(num='123'), test='abc${num NOT CLOSING').render('test')
+
+
+def test_parse_error2():
+    with pytest.raises(TemplateError):
+        MakoTemplates(_bind_to=Block(num='123'), test='abc${ WRONG_VAR }').render('test')
diff --git a/grc/tests/test_cheetah_converter.py b/grc/tests/test_cheetah_converter.py
new file mode 100644
index 0000000000..7999955436
--- /dev/null
+++ b/grc/tests/test_cheetah_converter.py
@@ -0,0 +1,132 @@
+""""""
+
+import functools
+import grc.converter.cheetah_converter as parser
+
+
+def test_basic():
+    c = parser.Converter(names={'abc'})
+    for convert in (c.convert_simple, c.convert_hard, c.to_python):
+        assert 'abc' == convert('$abc')
+        assert 'abc' == convert('$abc()')
+        assert 'abc' == convert('$(abc)')
+        assert 'abc' == convert('$(abc())')
+        assert 'abc' == convert('${abc}')
+        assert 'abc' == convert('${abc()}')
+
+    assert c.stats['simple'] == 2 * 6
+    assert c.stats['hard'] == 1 * 6
+
+
+def test_simple():
+    convert = parser.Converter(names={'abc': {'def'}})
+    assert 'abc' == convert.convert_simple('$abc')
+    assert 'abc.def' == convert.convert_simple('$abc.def')
+    assert 'abc.def' == convert.convert_simple('$(abc.def)')
+    assert 'abc.def' == convert.convert_simple('${abc.def}')
+    try:
+        convert.convert_simple('$abc.not_a_sub_key')
+    except NameError:
+        assert True
+    else:
+        assert False
+
+
+def test_conditional():
+    convert = parser.Converter(names={'abc'})
+    assert '(asb_asd_ if abc > 0 else __not__)' == convert.convert_inline_conditional(
+        '#if $abc > 0 then asb_$asd_ else __not__')
+
+
+def test_simple_format_string():
+    convert = functools.partial(parser.Converter(names={'abc'}).convert_simple, spec=parser.FormatString)
+    assert '{abc}' == convert('$abc')
+    assert '{abc:eval}' == convert('$abc()')
+    assert '{abc}' == convert('$(abc)')
+    assert '{abc:eval}' == convert('$(abc())')
+    assert '{abc}' == convert('${abc}')
+    assert '{abc:eval}' == convert('${abc()}')
+
+
+def test_hard_format_string():
+    names = {'abc': {'ff'}, 'param1': {}, 'param2': {}}
+    convert = functools.partial(parser.Converter(names).convert_hard, spec=parser.FormatString)
+    assert 'make_a_cool_block_{abc.ff}({param1}, {param2})' == \
+           convert('make_a_cool_block_${abc.ff}($param1, $param2)')
+
+
+converter = parser.Converter(names={'abc'})
+c2p = converter.to_python
+
+
+def test_opts():
+    assert 'abc abc abc' == c2p('$abc $(abc) ${abc}')
+    assert 'abc abc.abc abc' == c2p('$abc $abc.abc ${abc}')
+    assert 'abc abc[''].abc abc' == c2p('$abc $abc[''].abc() ${abc}')
+
+
+def test_nested():
+    assert 'abc(abc) abc + abc abc[abc]' == c2p('$abc($abc) $(abc + $abc) ${abc[$abc]}')
+    assert '(abc_abc_)' == c2p('(abc_$(abc)_)')
+
+
+def test_nested2():
+    class Other(parser.Python):
+        nested_start = '{'
+        nested_end = '}'
+    assert 'abc({abc})' == converter.convert('$abc($abc)', spec=Other)
+
+
+def test_nested3():
+    class Other(parser.Python):
+        start = '{'
+        end = '}'
+    assert '{abc(abc)}' == converter.convert('$abc($abc)', spec=Other)
+
+
+def test_with_string():
+    assert 'abc "$(abc)" abc' == c2p('$abc "$(abc)" ${abc}')
+    assert 'abc \'$(abc)\' abc' == c2p('$abc \'$(abc)\' ${abc}')
+    assert 'abc "\'\'$(abc)" abc' == c2p('$abc "\'\'$(abc)" ${abc}')
+
+
+def test_if():
+    result = converter.to_mako("""
+        #if $abc > 0
+            test
+        #else if $abc < 0
+            test
+        #else
+            bla
+        #end if
+    """)
+
+    expected = """
+        % if abc > 0:
+            test
+        % elif abc < 0:
+            test
+        % else:
+            bla
+        % endif
+    """
+    assert result == expected
+
+
+def test_hash_end():
+    result = converter.to_mako('$abc#slurp')
+    assert result == '${abc}\\'
+
+
+def test_slurp_if():
+    result = converter.to_mako("""
+        $abc#slurp
+        #if $abc
+    """)
+
+    expected = """
+        ${abc}
+        % if abc:
+    """
+    assert result == expected
+
diff --git a/grc/tests/test_evaled_property.py b/grc/tests/test_evaled_property.py
new file mode 100644
index 0000000000..27957cd291
--- /dev/null
+++ b/grc/tests/test_evaled_property.py
@@ -0,0 +1,104 @@
+import collections
+import numbers
+
+from grc.core.utils.descriptors import Evaluated, EvaluatedEnum, EvaluatedPInt
+
+
+class A(object):
+    def __init__(self, **kwargs):
+        self.called = collections.defaultdict(int)
+        self.errors = []
+        self.namespace = kwargs
+
+    def add_error_message(self, msg):
+        self.errors.append(msg)
+
+    @property
+    def parent_block(self):
+        return self
+
+    def evaluate(self, expr):
+        self.called['evaluate'] += 1
+        return eval(expr, self.namespace)
+
+    @Evaluated(int, 1)
+    def foo(self):
+        self.called['foo'] += 1
+        return eval(self._foo)
+
+    bar = Evaluated(numbers.Real, 1.0, name='bar')
+
+    test = EvaluatedEnum(['a', 'b'], 'a', name='test')
+
+    lala = EvaluatedPInt()
+
+
+def test_fixed_value():
+    a = A()
+    a.foo = 10
+
+    assert not hasattr(a, '_foo')
+    assert a.foo == 10
+    assert a.called['foo'] == 0
+    delattr(a, 'foo')
+    assert a.foo == 10
+    assert a.called['foo'] == 0
+
+
+def test_evaled():
+    a = A()
+    a.foo = '${ 10 + 1 }'
+    assert getattr(a, '_foo') == '10 + 1'
+    assert a.foo == 11 and a.foo == 11
+    assert a.called['foo'] == 1
+    assert a.called['evaluate'] == 0
+    delattr(a, 'foo')
+    assert a.foo == 11 and a.foo == 11
+    assert a.called['foo'] == 2
+    assert not a.errors
+
+
+def test_evaled_with_default():
+    a = A()
+    a.bar = '${ 10 + 1 }'
+    assert getattr(a, '_bar') == '10 + 1'
+    assert a.bar == 11.0 and type(a.bar) == int
+    assert a.called['evaluate'] == 1
+    assert not a.errors
+
+
+def test_evaled_int_with_default():
+    a = A(ll=10)
+    a.lala = '${ ll * 2 }'
+    assert a.lala == 20
+    a.namespace['ll'] = -10
+    assert a.lala == 20
+    del a.lala
+    assert a.lala == 1
+    assert not a.errors
+
+
+def test_evaled_enum_fixed_value():
+    a = A()
+    a.test = 'a'
+    assert not hasattr(a, '_test')
+    assert a.test == 'a' and type(a.test) == str
+    assert not a.errors
+
+
+def test_evaled_enum():
+    a = A(bla=False)
+    a.test = '${ "a" if bla else "b" }'
+    assert a.test == 'b'
+    a.namespace['bla'] = True
+    assert a.test == 'b'
+    del a.test
+    assert a.test == 'a'
+    assert not a.errors
+
+
+def test_class_access():
+    a = A()
+    a.foo = '${ meme }'
+    descriptor = getattr(a.__class__, 'foo')
+    assert descriptor.name_raw == '_foo'
diff --git a/grc/tests/test_expr_utils.py b/grc/tests/test_expr_utils.py
new file mode 100644
index 0000000000..4f25477bf1
--- /dev/null
+++ b/grc/tests/test_expr_utils.py
@@ -0,0 +1,41 @@
+import operator
+
+import pytest
+
+from grc.core.utils import expr_utils
+
+id_getter = operator.itemgetter(0)
+expr_getter = operator.itemgetter(1)
+
+
+def test_simple():
+    objects = [
+        ['c', '2 * a + b'],
+        ['a', '1'],
+        ['b', '2 * a + unknown * d'],
+        ['d', '5'],
+    ]
+
+    expected = [
+        ['a', '1'],
+        ['d', '5'],
+        ['b', '2 * a + unknown * d'],
+        ['c', '2 * a + b'],
+    ]
+
+    out = expr_utils.sort_objects2(objects, id_getter, expr_getter)
+
+    assert out == expected
+
+
+def test_other():
+    test = [
+        ['c', '2 * a + b'],
+        ['a', '1'],
+        ['b', '2 * c + unknown'],
+    ]
+
+    expr_utils.sort_objects2(test, id_getter, expr_getter, check_circular=False)
+
+    with pytest.raises(RuntimeError):
+        expr_utils.sort_objects2(test, id_getter, expr_getter)
diff --git a/grc/tests/test_generator.py b/grc/tests/test_generator.py
new file mode 100644
index 0000000000..4c79ce4bd3
--- /dev/null
+++ b/grc/tests/test_generator.py
@@ -0,0 +1,46 @@
+# Copyright 2016 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio 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, or (at your option)
+# any later version.
+#
+# GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+from os import path
+import tempfile
+
+from grc.core.platform import Platform
+
+
+def test_generator():
+    # c&p form compiler code.
+    # todo: make this independent from installed GR
+    grc_file = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
+    out_dir = tempfile.gettempdir()
+
+    platform = Platform(
+        name='GNU Radio Companion Compiler',
+        prefs=None,
+        version='0.0.0',
+    )
+    platform.build_library()
+
+    flow_graph = platform.make_flow_graph(grc_file)
+    flow_graph.rewrite()
+    flow_graph.validate()
+
+    assert flow_graph.is_valid()
+
+    generator = platform.Generator(flow_graph, path.join(path.dirname(__file__), 'resources'))
+    generator.write()
diff --git a/grc/tests/test_xml_parser.py b/grc/tests/test_xml_parser.py
new file mode 100644
index 0000000000..c68b6cdc5a
--- /dev/null
+++ b/grc/tests/test_xml_parser.py
@@ -0,0 +1,39 @@
+# Copyright 2017 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion 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.
+#
+# GNU Radio Companion 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
+
+from os import path
+import sys
+
+from grc.converter import flow_graph
+
+
+def test_flow_graph_converter():
+    filename = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
+
+    data = flow_graph.from_xml(filename)
+
+    flow_graph.dump(data, sys.stdout)
+
+
+def test_flow_graph_converter_with_fp():
+    filename = path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')
+
+    with open(filename) as fp:
+        data = flow_graph.from_xml(fp)
+
+    flow_graph.dump(data, sys.stdout)
+
diff --git a/grc/tests/test_yaml_checker.py b/grc/tests/test_yaml_checker.py
new file mode 100644
index 0000000000..e6b466e511
--- /dev/null
+++ b/grc/tests/test_yaml_checker.py
@@ -0,0 +1,84 @@
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion 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.
+#
+# GNU Radio Companion 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 yaml
+
+from grc.core.schema_checker import Validator, BLOCK_SCHEME
+
+
+BLOCK1 = """
+id: block_key
+label: testname
+
+parameters:
+-   id: vlen
+    label: Vec Length
+    dtype: int
+    default: 1
+-   id: out_type
+    label: Vec Length
+    dtype: string
+    default: complex
+-   id: a
+    label: Alpha
+    dtype: ${ out_type }
+    default: '0'
+
+inputs:
+-   label: in
+    domain: stream
+    dtype: complex
+    vlen: ${ 2 * vlen }
+-   name: in2
+    domain: message
+    id: in2
+
+outputs:
+-   label: out
+    domain: stream
+    dtype: ${ out_type }
+    vlen: ${ vlen }
+
+templates:
+    make: blocks.complex_to_mag_squared(${ vlen })
+    
+file_format: 1
+"""
+
+
+def test_min():
+    checker = Validator(BLOCK_SCHEME)
+    assert checker.run({'id': 'test', 'file_format': 1}), checker.messages
+    assert not checker.run({'name': 'test', 'file_format': 1})
+
+
+def test_extra_keys():
+    checker = Validator(BLOCK_SCHEME)
+    assert checker.run({'id': 'test', 'abcdefg': 'nonsense', 'file_format': 1})
+    assert checker.messages == [('block', 'warn', "Ignoring extra key 'abcdefg'")]
+
+
+def test_checker():
+    checker = Validator(BLOCK_SCHEME)
+    data = yaml.load(BLOCK1)
+    passed = checker.run(data)
+    if not passed:
+        print()
+        for msg in checker.messages:
+            print(msg)
+
+    assert passed, checker.messages
-- 
cgit v1.2.3