diff mbox

[v8,08/40] qapi: Add some union tests

Message ID 1430751937-17523-9-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake May 4, 2015, 3:05 p.m. UTC
Demonstrate that the qapi generator doesn't deal well with unions
that aren't up to par. Later patches will update the expected
reseults as the generator is made stricter.  A few tests work
as planned, but most show poor or missing error messages.

Of particular note, qapi-code-gen.txt documents 'base' only for
flat unions, but the tests here demonstrate that we currently allow
a 'base' to a simple union, although it is exercised only in the
testsuite.  Later patches will remove this undocumented feature, to
give us more flexibility in adding other future extensions to union
types.  For example, one possible extension is the idea of a
type-safe simple enum, where added fields tie the discriminator to
a user-defined enum type rather than creating an implicit enum from
the names in 'data'.  But adding such safety on top of a simple
enum with a base type could look ambiguous with a flat enum;
besides, the documentation also mentions how any simple union can
be represented by an equivalent flat union.  So it will be simpler
to just outlaw support for something we aren't using.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>

---

v7: reduce comment churn in alternate-array
---
 tests/Makefile                                           | 14 +++++++++++---
 tests/qapi-schema/alternate-array.err                    |  0
 tests/qapi-schema/alternate-array.exit                   |  1 +
 tests/qapi-schema/alternate-array.json                   |  8 ++++++++
 tests/qapi-schema/alternate-array.out                    |  4 ++++
 tests/qapi-schema/alternate-base.err                     |  0
 tests/qapi-schema/alternate-base.exit                    |  1 +
 tests/qapi-schema/alternate-base.json                    |  7 +++++++
 tests/qapi-schema/alternate-base.out                     |  4 ++++
 tests/qapi-schema/alternate-clash.err                    |  0
 tests/qapi-schema/alternate-clash.exit                   |  1 +
 tests/qapi-schema/alternate-clash.json                   |  4 ++++
 tests/qapi-schema/alternate-clash.out                    |  3 +++
 tests/qapi-schema/alternate-conflict-dict.err            |  0
 tests/qapi-schema/alternate-conflict-dict.exit           |  1 +
 tests/qapi-schema/alternate-conflict-dict.json           |  9 +++++++++
 tests/qapi-schema/alternate-conflict-dict.out            |  6 ++++++
 tests/qapi-schema/alternate-conflict-string.err          |  0
 tests/qapi-schema/alternate-conflict-string.exit         |  1 +
 tests/qapi-schema/alternate-conflict-string.json         |  7 +++++++
 tests/qapi-schema/alternate-conflict-string.out          |  5 +++++
 tests/qapi-schema/alternate-good.err                     |  0
 tests/qapi-schema/alternate-good.exit                    |  1 +
 tests/qapi-schema/alternate-good.json                    | 10 ++++++++++
 tests/qapi-schema/alternate-good.out                     |  6 ++++++
 tests/qapi-schema/alternate-nested.err                   |  0
 tests/qapi-schema/alternate-nested.exit                  |  1 +
 tests/qapi-schema/alternate-nested.json                  |  7 +++++++
 tests/qapi-schema/alternate-nested.out                   |  5 +++++
 tests/qapi-schema/alternate-unknown.err                  |  0
 tests/qapi-schema/alternate-unknown.exit                 |  1 +
 tests/qapi-schema/alternate-unknown.json                 |  4 ++++
 tests/qapi-schema/alternate-unknown.out                  |  3 +++
 tests/qapi-schema/flat-union-bad-base.err                |  1 +
 tests/qapi-schema/flat-union-bad-base.exit               |  1 +
 tests/qapi-schema/flat-union-bad-base.json               | 13 +++++++++++++
 tests/qapi-schema/flat-union-bad-base.out                |  0
 tests/qapi-schema/flat-union-bad-discriminator.err       |  0
 tests/qapi-schema/flat-union-bad-discriminator.exit      |  1 +
 tests/qapi-schema/flat-union-bad-discriminator.json      | 14 ++++++++++++++
 tests/qapi-schema/flat-union-bad-discriminator.out       | 10 ++++++++++
 tests/qapi-schema/flat-union-base-star.err               |  1 +
 tests/qapi-schema/flat-union-base-star.exit              |  1 +
 tests/qapi-schema/flat-union-base-star.json              | 12 ++++++++++++
 tests/qapi-schema/flat-union-base-star.out               |  0
 tests/qapi-schema/flat-union-base-union.err              |  1 +
 tests/qapi-schema/flat-union-base-union.exit             |  1 +
 tests/qapi-schema/flat-union-base-union.json             | 15 +++++++++++++++
 tests/qapi-schema/flat-union-base-union.out              |  0
 tests/qapi-schema/flat-union-branch-clash.err            |  0
 tests/qapi-schema/flat-union-branch-clash.exit           |  1 +
 tests/qapi-schema/flat-union-branch-clash.json           | 14 ++++++++++++++
 tests/qapi-schema/flat-union-branch-clash.out            |  9 +++++++++
 tests/qapi-schema/flat-union-inline.err                  |  1 +
 tests/qapi-schema/flat-union-inline.exit                 |  1 +
 tests/qapi-schema/flat-union-inline.json                 | 11 +++++++++++
 tests/qapi-schema/flat-union-inline.out                  |  0
 tests/qapi-schema/flat-union-int-branch.err              |  0
 tests/qapi-schema/flat-union-int-branch.exit             |  1 +
 tests/qapi-schema/flat-union-int-branch.json             | 12 ++++++++++++
 tests/qapi-schema/flat-union-int-branch.out              |  7 +++++++
 tests/qapi-schema/flat-union-no-base.err                 |  2 +-
 tests/qapi-schema/flat-union-no-base.json                |  8 +++++---
 tests/qapi-schema/flat-union-optional-discriminator.err  |  0
 tests/qapi-schema/flat-union-optional-discriminator.exit |  1 +
 tests/qapi-schema/flat-union-optional-discriminator.json | 10 ++++++++++
 tests/qapi-schema/flat-union-optional-discriminator.out  |  7 +++++++
 tests/qapi-schema/union-bad-branch.err                   |  0
 tests/qapi-schema/union-bad-branch.exit                  |  1 +
 tests/qapi-schema/union-bad-branch.json                  |  8 ++++++++
 tests/qapi-schema/union-bad-branch.out                   |  6 ++++++
 tests/qapi-schema/union-base-no-discriminator.err        |  0
 tests/qapi-schema/union-base-no-discriminator.exit       |  1 +
 tests/qapi-schema/union-base-no-discriminator.json       | 14 ++++++++++++++
 tests/qapi-schema/union-base-no-discriminator.out        |  8 ++++++++
 tests/qapi-schema/union-invalid-base.err                 |  2 +-
 tests/qapi-schema/union-invalid-base.json                |  4 +++-
 tests/qapi-schema/union-max.err                          |  0
 tests/qapi-schema/union-max.exit                         |  1 +
 tests/qapi-schema/union-max.json                         |  3 +++
 tests/qapi-schema/union-max.out                          |  3 +++
 tests/qapi-schema/union-optional-branch.err              |  0
 tests/qapi-schema/union-optional-branch.exit             |  1 +
 tests/qapi-schema/union-optional-branch.json             |  2 ++
 tests/qapi-schema/union-optional-branch.out              |  3 +++
 tests/qapi-schema/union-unknown.err                      |  0
 tests/qapi-schema/union-unknown.exit                     |  1 +
 tests/qapi-schema/union-unknown.json                     |  3 +++
 tests/qapi-schema/union-unknown.out                      |  3 +++
 89 files changed, 325 insertions(+), 9 deletions(-)
 create mode 100644 tests/qapi-schema/alternate-array.err
 create mode 100644 tests/qapi-schema/alternate-array.exit
 create mode 100644 tests/qapi-schema/alternate-array.json
 create mode 100644 tests/qapi-schema/alternate-array.out
 create mode 100644 tests/qapi-schema/alternate-base.err
 create mode 100644 tests/qapi-schema/alternate-base.exit
 create mode 100644 tests/qapi-schema/alternate-base.json
 create mode 100644 tests/qapi-schema/alternate-base.out
 create mode 100644 tests/qapi-schema/alternate-clash.err
 create mode 100644 tests/qapi-schema/alternate-clash.exit
 create mode 100644 tests/qapi-schema/alternate-clash.json
 create mode 100644 tests/qapi-schema/alternate-clash.out
 create mode 100644 tests/qapi-schema/alternate-conflict-dict.err
 create mode 100644 tests/qapi-schema/alternate-conflict-dict.exit
 create mode 100644 tests/qapi-schema/alternate-conflict-dict.json
 create mode 100644 tests/qapi-schema/alternate-conflict-dict.out
 create mode 100644 tests/qapi-schema/alternate-conflict-string.err
 create mode 100644 tests/qapi-schema/alternate-conflict-string.exit
 create mode 100644 tests/qapi-schema/alternate-conflict-string.json
 create mode 100644 tests/qapi-schema/alternate-conflict-string.out
 create mode 100644 tests/qapi-schema/alternate-good.err
 create mode 100644 tests/qapi-schema/alternate-good.exit
 create mode 100644 tests/qapi-schema/alternate-good.json
 create mode 100644 tests/qapi-schema/alternate-good.out
 create mode 100644 tests/qapi-schema/alternate-nested.err
 create mode 100644 tests/qapi-schema/alternate-nested.exit
 create mode 100644 tests/qapi-schema/alternate-nested.json
 create mode 100644 tests/qapi-schema/alternate-nested.out
 create mode 100644 tests/qapi-schema/alternate-unknown.err
 create mode 100644 tests/qapi-schema/alternate-unknown.exit
 create mode 100644 tests/qapi-schema/alternate-unknown.json
 create mode 100644 tests/qapi-schema/alternate-unknown.out
 create mode 100644 tests/qapi-schema/flat-union-bad-base.err
 create mode 100644 tests/qapi-schema/flat-union-bad-base.exit
 create mode 100644 tests/qapi-schema/flat-union-bad-base.json
 create mode 100644 tests/qapi-schema/flat-union-bad-base.out
 create mode 100644 tests/qapi-schema/flat-union-bad-discriminator.err
 create mode 100644 tests/qapi-schema/flat-union-bad-discriminator.exit
 create mode 100644 tests/qapi-schema/flat-union-bad-discriminator.json
 create mode 100644 tests/qapi-schema/flat-union-bad-discriminator.out
 create mode 100644 tests/qapi-schema/flat-union-base-star.err
 create mode 100644 tests/qapi-schema/flat-union-base-star.exit
 create mode 100644 tests/qapi-schema/flat-union-base-star.json
 create mode 100644 tests/qapi-schema/flat-union-base-star.out
 create mode 100644 tests/qapi-schema/flat-union-base-union.err
 create mode 100644 tests/qapi-schema/flat-union-base-union.exit
 create mode 100644 tests/qapi-schema/flat-union-base-union.json
 create mode 100644 tests/qapi-schema/flat-union-base-union.out
 create mode 100644 tests/qapi-schema/flat-union-branch-clash.err
 create mode 100644 tests/qapi-schema/flat-union-branch-clash.exit
 create mode 100644 tests/qapi-schema/flat-union-branch-clash.json
 create mode 100644 tests/qapi-schema/flat-union-branch-clash.out
 create mode 100644 tests/qapi-schema/flat-union-inline.err
 create mode 100644 tests/qapi-schema/flat-union-inline.exit
 create mode 100644 tests/qapi-schema/flat-union-inline.json
 create mode 100644 tests/qapi-schema/flat-union-inline.out
 create mode 100644 tests/qapi-schema/flat-union-int-branch.err
 create mode 100644 tests/qapi-schema/flat-union-int-branch.exit
 create mode 100644 tests/qapi-schema/flat-union-int-branch.json
 create mode 100644 tests/qapi-schema/flat-union-int-branch.out
 create mode 100644 tests/qapi-schema/flat-union-optional-discriminator.err
 create mode 100644 tests/qapi-schema/flat-union-optional-discriminator.exit
 create mode 100644 tests/qapi-schema/flat-union-optional-discriminator.json
 create mode 100644 tests/qapi-schema/flat-union-optional-discriminator.out
 create mode 100644 tests/qapi-schema/union-bad-branch.err
 create mode 100644 tests/qapi-schema/union-bad-branch.exit
 create mode 100644 tests/qapi-schema/union-bad-branch.json
 create mode 100644 tests/qapi-schema/union-bad-branch.out
 create mode 100644 tests/qapi-schema/union-base-no-discriminator.err
 create mode 100644 tests/qapi-schema/union-base-no-discriminator.exit
 create mode 100644 tests/qapi-schema/union-base-no-discriminator.json
 create mode 100644 tests/qapi-schema/union-base-no-discriminator.out
 create mode 100644 tests/qapi-schema/union-max.err
 create mode 100644 tests/qapi-schema/union-max.exit
 create mode 100644 tests/qapi-schema/union-max.json
 create mode 100644 tests/qapi-schema/union-max.out
 create mode 100644 tests/qapi-schema/union-optional-branch.err
 create mode 100644 tests/qapi-schema/union-optional-branch.exit
 create mode 100644 tests/qapi-schema/union-optional-branch.json
 create mode 100644 tests/qapi-schema/union-optional-branch.out
 create mode 100644 tests/qapi-schema/union-unknown.err
 create mode 100644 tests/qapi-schema/union-unknown.exit
 create mode 100644 tests/qapi-schema/union-unknown.json
 create mode 100644 tests/qapi-schema/union-unknown.out
diff mbox

Patch

diff --git a/tests/Makefile b/tests/Makefile
index 3865a61..3978c3d 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -217,10 +217,18 @@  check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
 	qapi-schema-test.json quoted-structural-chars.json \
 	trailing-comma-list.json trailing-comma-object.json \
 	unclosed-list.json unclosed-object.json unclosed-string.json \
-	duplicate-key.json union-invalid-base.json flat-union-no-base.json \
-	flat-union-invalid-discriminator.json \
+	duplicate-key.json union-invalid-base.json union-bad-branch.json \
+	union-optional-branch.json union-unknown.json union-max.json \
+	flat-union-optional-discriminator.json flat-union-no-base.json \
+	flat-union-invalid-discriminator.json flat-union-inline.json \
 	flat-union-invalid-branch-key.json flat-union-reverse-define.json \
-	flat-union-string-discriminator.json \
+	flat-union-string-discriminator.json union-base-no-discriminator.json \
+	flat-union-bad-discriminator.json flat-union-bad-base.json \
+	flat-union-base-star.json flat-union-int-branch.json \
+	flat-union-base-union.json flat-union-branch-clash.json \
+	alternate-nested.json alternate-unknown.json alternate-clash.json \
+	alternate-good.json alternate-base.json alternate-array.json \
+	alternate-conflict-string.json alternate-conflict-dict.json \
 	include-simple.json include-relpath.json include-format-err.json \
 	include-non-file.json include-no-file.json include-before-err.json \
 	include-nested-err.json include-self-cycle.json include-cycle.json \
diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/alternate-array.exit b/tests/qapi-schema/alternate-array.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/alternate-array.json b/tests/qapi-schema/alternate-array.json
new file mode 100644
index 0000000..25224c6
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.json
@@ -0,0 +1,8 @@ 
+# FIXME: we should not allow array branches in anonymous unions
+# TODO: should we support this?
+{ 'type': 'One',
+  'data': { 'name': 'str' } }
+{ 'union': 'MyUnion',
+  'discriminator': {},
+  'data': { 'one': 'One',
+            'two': [ 'int' ] } }
diff --git a/tests/qapi-schema/alternate-array.out b/tests/qapi-schema/alternate-array.out
new file mode 100644
index 0000000..90dc22c
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.out
@@ -0,0 +1,4 @@ 
+[OrderedDict([('type', 'One'), ('data', OrderedDict([('name', 'str')]))]),
+ OrderedDict([('union', 'MyUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'One'), ('two', ['int'])]))])]
+[{'enum_name': 'MyUnionKind', 'enum_values': None}]
+[OrderedDict([('type', 'One'), ('data', OrderedDict([('name', 'str')]))])]
diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/alternate-base.exit b/tests/qapi-schema/alternate-base.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/alternate-base.json b/tests/qapi-schema/alternate-base.json
new file mode 100644
index 0000000..2d36db1
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.json
@@ -0,0 +1,7 @@ 
+# FIXME: we should reject anonymous union with base type
+{ 'type': 'Base',
+  'data': { 'string': 'str' } }
+{ 'union': 'MyUnion',
+  'base': 'Base',
+  'discriminator': {},
+  'data': { 'number': 'int' } }
diff --git a/tests/qapi-schema/alternate-base.out b/tests/qapi-schema/alternate-base.out
new file mode 100644
index 0000000..7fb31f5
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.out
@@ -0,0 +1,4 @@ 
+[OrderedDict([('type', 'Base'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('union', 'MyUnion'), ('base', 'Base'), ('discriminator', OrderedDict()), ('data', OrderedDict([('number', 'int')]))])]
+[{'enum_name': 'MyUnionKind', 'enum_values': None}]
+[OrderedDict([('type', 'Base'), ('data', OrderedDict([('string', 'str')]))])]
diff --git a/tests/qapi-schema/alternate-clash.err b/tests/qapi-schema/alternate-clash.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/alternate-clash.exit b/tests/qapi-schema/alternate-clash.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/alternate-clash.json b/tests/qapi-schema/alternate-clash.json
new file mode 100644
index 0000000..7e2ef23
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.json
@@ -0,0 +1,4 @@ 
+# FIXME: we should detect C enum collisions in an anonymous union
+{ 'union': 'Union1',
+  'discriminator': {},
+  'data': { 'one': 'str', 'ONE': 'int' } }
diff --git a/tests/qapi-schema/alternate-clash.out b/tests/qapi-schema/alternate-clash.out
new file mode 100644
index 0000000..c6687fa
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.out
@@ -0,0 +1,3 @@ 
+[OrderedDict([('union', 'Union1'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'str'), ('ONE', 'int')]))])]
+[{'enum_name': 'Union1Kind', 'enum_values': None}]
+[]
diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/alternate-conflict-dict.exit b/tests/qapi-schema/alternate-conflict-dict.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/alternate-conflict-dict.json b/tests/qapi-schema/alternate-conflict-dict.json
new file mode 100644
index 0000000..d2ed9de
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.json
@@ -0,0 +1,9 @@ 
+# FIXME: we should reject anonymous unions with multiple object branches
+{ 'type': 'One',
+  'data': { 'name': 'str' } }
+{ 'type': 'Two',
+  'data': { 'value': 'int' } }
+{ 'union': 'MyUnion',
+  'discriminator': {},
+  'data': { 'one': 'One',
+            'two': 'Two' } }
diff --git a/tests/qapi-schema/alternate-conflict-dict.out b/tests/qapi-schema/alternate-conflict-dict.out
new file mode 100644
index 0000000..b9ac945
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.out
@@ -0,0 +1,6 @@ 
+[OrderedDict([('type', 'One'), ('data', OrderedDict([('name', 'str')]))]),
+ OrderedDict([('type', 'Two'), ('data', OrderedDict([('value', 'int')]))]),
+ OrderedDict([('union', 'MyUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'One'), ('two', 'Two')]))])]
+[{'enum_name': 'MyUnionKind', 'enum_values': None}]
+[OrderedDict([('type', 'One'), ('data', OrderedDict([('name', 'str')]))]),
+ OrderedDict([('type', 'Two'), ('data', OrderedDict([('value', 'int')]))])]
diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/alternate-conflict-string.exit b/tests/qapi-schema/alternate-conflict-string.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/alternate-conflict-string.json b/tests/qapi-schema/alternate-conflict-string.json
new file mode 100644
index 0000000..35245a3
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.json
@@ -0,0 +1,7 @@ 
+# FIXME: we should reject anonymous unions with multiple string-like branches
+{ 'enum': 'Enum',
+  'data': [ 'hello', 'world' ] }
+{ 'union': 'MyUnion',
+  'discriminator': {},
+  'data': { 'one': 'str',
+            'two': 'Enum' } }
diff --git a/tests/qapi-schema/alternate-conflict-string.out b/tests/qapi-schema/alternate-conflict-string.out
new file mode 100644
index 0000000..e7b39a2
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.out
@@ -0,0 +1,5 @@ 
+[OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
+ OrderedDict([('union', 'MyUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('one', 'str'), ('two', 'Enum')]))])]
+[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
+ {'enum_name': 'MyUnionKind', 'enum_values': None}]
+[]
diff --git a/tests/qapi-schema/alternate-good.err b/tests/qapi-schema/alternate-good.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/alternate-good.exit b/tests/qapi-schema/alternate-good.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/alternate-good.json b/tests/qapi-schema/alternate-good.json
new file mode 100644
index 0000000..73d0993
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.json
@@ -0,0 +1,10 @@ 
+# Working example of anonymous union
+{ 'type': 'Data',
+  'data': { '*number': 'int', '*name': 'str' } }
+{ 'enum': 'Enum',
+  'data': [ 'hello', 'world' ] }
+{ 'union': 'MyUnion',
+  'discriminator': {},
+  'data': { 'value': 'int',
+            'string': 'Enum',
+            'struct': 'Data' } }
diff --git a/tests/qapi-schema/alternate-good.out b/tests/qapi-schema/alternate-good.out
new file mode 100644
index 0000000..b5117d1
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.out
@@ -0,0 +1,6 @@ 
+[OrderedDict([('type', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))]),
+ OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
+ OrderedDict([('union', 'MyUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('value', 'int'), ('string', 'Enum'), ('struct', 'Data')]))])]
+[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
+ {'enum_name': 'MyUnionKind', 'enum_values': None}]
+[OrderedDict([('type', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))])]
diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/alternate-nested.exit b/tests/qapi-schema/alternate-nested.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/alternate-nested.json b/tests/qapi-schema/alternate-nested.json
new file mode 100644
index 0000000..d5812bf
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.json
@@ -0,0 +1,7 @@ 
+# FIXME: we should reject a nested anonymous union branch
+{ 'union': 'Union1',
+  'discriminator': {},
+  'data': { 'name': 'str', 'value': 'int' } }
+{ 'union': 'Union2',
+  'discriminator': {},
+  'data': { 'nested': 'Union1' } }
diff --git a/tests/qapi-schema/alternate-nested.out b/tests/qapi-schema/alternate-nested.out
new file mode 100644
index 0000000..0137c1f
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.out
@@ -0,0 +1,5 @@ 
+[OrderedDict([('union', 'Union1'), ('discriminator', OrderedDict()), ('data', OrderedDict([('name', 'str'), ('value', 'int')]))]),
+ OrderedDict([('union', 'Union2'), ('discriminator', OrderedDict()), ('data', OrderedDict([('nested', 'Union1')]))])]
+[{'enum_name': 'Union1Kind', 'enum_values': None},
+ {'enum_name': 'Union2Kind', 'enum_values': None}]
+[]
diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/alternate-unknown.exit b/tests/qapi-schema/alternate-unknown.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/alternate-unknown.json b/tests/qapi-schema/alternate-unknown.json
new file mode 100644
index 0000000..0bab9c2
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.json
@@ -0,0 +1,4 @@ 
+# FIXME: we should reject an anonymous union with unknown type in branch
+{ 'union': 'Union',
+  'discriminator': {},
+  'data': { 'unknown': 'MissingType' } }
diff --git a/tests/qapi-schema/alternate-unknown.out b/tests/qapi-schema/alternate-unknown.out
new file mode 100644
index 0000000..0911cdc
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.out
@@ -0,0 +1,3 @@ 
+[OrderedDict([('union', 'Union'), ('discriminator', OrderedDict()), ('data', OrderedDict([('unknown', 'MissingType')]))])]
+[{'enum_name': 'UnionKind', 'enum_values': None}]
+[]
diff --git a/tests/qapi-schema/flat-union-bad-base.err b/tests/qapi-schema/flat-union-bad-base.err
new file mode 100644
index 0000000..5962ff4
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/flat-union-bad-base.json:9: Base 'OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')])' is not a valid type
diff --git a/tests/qapi-schema/flat-union-bad-base.exit b/tests/qapi-schema/flat-union-bad-base.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.exit
@@ -0,0 +1 @@ 
+1
diff --git a/tests/qapi-schema/flat-union-bad-base.json b/tests/qapi-schema/flat-union-bad-base.json
new file mode 100644
index 0000000..6c14132
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.json
@@ -0,0 +1,13 @@ 
+# FIXME: poor message: we require the base to be an existing complex type
+# TODO: should we allow an anonymous inline base type?
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'type': 'TestTypeA',
+  'data': { 'string': 'str' } }
+{ 'type': 'TestTypeB',
+  'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+  'base': { 'enum1': 'TestEnum', 'kind': 'str' },
+  'discriminator': 'enum1',
+  'data': { 'value1': 'TestTypeA',
+            'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-bad-base.out b/tests/qapi-schema/flat-union-bad-base.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.err b/tests/qapi-schema/flat-union-bad-discriminator.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.exit b/tests/qapi-schema/flat-union-bad-discriminator.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.json b/tests/qapi-schema/flat-union-bad-discriminator.json
new file mode 100644
index 0000000..1599a59
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.json
@@ -0,0 +1,14 @@ 
+# FIXME: we should require the discriminator to be a string naming a base-type member
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'type': 'TestBase',
+  'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+{ 'type': 'TestTypeA',
+  'data': { 'string': 'str' } }
+{ 'type': 'TestTypeB',
+  'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+  'base': 'TestBase',
+  'discriminator': [],
+  'data': { 'kind1': 'TestTypeA',
+            'kind2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.out b/tests/qapi-schema/flat-union-bad-discriminator.out
new file mode 100644
index 0000000..b6ce217
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.out
@@ -0,0 +1,10 @@ 
+[OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
+ OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')]))]),
+ OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('union', 'TestUnion'), ('base', 'TestBase'), ('discriminator', []), ('data', OrderedDict([('kind1', 'TestTypeA'), ('kind2', 'TestTypeB')]))])]
+[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']},
+ {'enum_name': 'TestUnionKind', 'enum_values': None}]
+[OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')]))]),
+ OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
diff --git a/tests/qapi-schema/flat-union-base-star.err b/tests/qapi-schema/flat-union-base-star.err
new file mode 100644
index 0000000..60e47ef
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid type
diff --git a/tests/qapi-schema/flat-union-base-star.exit b/tests/qapi-schema/flat-union-base-star.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.exit
@@ -0,0 +1 @@ 
+1
diff --git a/tests/qapi-schema/flat-union-base-star.json b/tests/qapi-schema/flat-union-base-star.json
new file mode 100644
index 0000000..994533a
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.json
@@ -0,0 +1,12 @@ 
+# we require the base to be an existing complex type
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'type': 'TestTypeA',
+  'data': { 'string': 'str' } }
+{ 'type': 'TestTypeB',
+  'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+  'base': '**',
+  'discriminator': 'enum1',
+  'data': { 'value1': 'TestTypeA',
+            'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-base-star.out b/tests/qapi-schema/flat-union-base-star.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-base-union.err b/tests/qapi-schema/flat-union-base-union.err
new file mode 100644
index 0000000..185bf51
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/flat-union-base-union.json:11: Base 'UnionBase' is not a valid type
diff --git a/tests/qapi-schema/flat-union-base-union.exit b/tests/qapi-schema/flat-union-base-union.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.exit
@@ -0,0 +1 @@ 
+1
diff --git a/tests/qapi-schema/flat-union-base-union.json b/tests/qapi-schema/flat-union-base-union.json
new file mode 100644
index 0000000..838986c
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.json
@@ -0,0 +1,15 @@ 
+# FIXME: the error message needs help: we require the base to be a struct
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'type': 'TestTypeA',
+  'data': { 'string': 'str' } }
+{ 'type': 'TestTypeB',
+  'data': { 'integer': 'int' } }
+{ 'union': 'UnionBase',
+  'data': { 'kind1': 'TestTypeA',
+            'kind2': 'TestTypeB' } }
+{ 'union': 'TestUnion',
+  'base': 'UnionBase',
+  'discriminator': 'type',
+  'data': { 'kind1': 'TestTypeA',
+            'kind2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-base-union.out b/tests/qapi-schema/flat-union-base-union.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-branch-clash.err b/tests/qapi-schema/flat-union-branch-clash.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-branch-clash.exit b/tests/qapi-schema/flat-union-branch-clash.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/flat-union-branch-clash.json b/tests/qapi-schema/flat-union-branch-clash.json
new file mode 100644
index 0000000..4091477
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.json
@@ -0,0 +1,14 @@ 
+# FIXME: we should check for no duplicate keys between branches and base
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'type': 'Base',
+  'data': { 'enum1': 'TestEnum', 'name': 'str' } }
+{ 'type': 'Branch1',
+  'data': { 'name': 'str' } }
+{ 'type': 'Branch2',
+  'data': { 'value': 'int' } }
+{ 'union': 'TestUnion',
+  'base': 'Base',
+  'discriminator': 'enum1',
+  'data': { 'value1': 'Branch1',
+            'value2': 'Branch2' } }
diff --git a/tests/qapi-schema/flat-union-branch-clash.out b/tests/qapi-schema/flat-union-branch-clash.out
new file mode 100644
index 0000000..5d54113
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.out
@@ -0,0 +1,9 @@ 
+[OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
+ OrderedDict([('type', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum'), ('name', 'str')]))]),
+ OrderedDict([('type', 'Branch1'), ('data', OrderedDict([('name', 'str')]))]),
+ OrderedDict([('type', 'Branch2'), ('data', OrderedDict([('value', 'int')]))]),
+ OrderedDict([('union', 'TestUnion'), ('base', 'Base'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'Branch1'), ('value2', 'Branch2')]))])]
+[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
+[OrderedDict([('type', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum'), ('name', 'str')]))]),
+ OrderedDict([('type', 'Branch1'), ('data', OrderedDict([('name', 'str')]))]),
+ OrderedDict([('type', 'Branch2'), ('data', OrderedDict([('value', 'int')]))])]
diff --git a/tests/qapi-schema/flat-union-inline.err b/tests/qapi-schema/flat-union-inline.err
new file mode 100644
index 0000000..51fbe54
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/flat-union-inline.json:7: Base 'OrderedDict([('enum1', 'TestEnum'), ('kind', 'str')])' is not a valid type
diff --git a/tests/qapi-schema/flat-union-inline.exit b/tests/qapi-schema/flat-union-inline.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.exit
@@ -0,0 +1 @@ 
+1
diff --git a/tests/qapi-schema/flat-union-inline.json b/tests/qapi-schema/flat-union-inline.json
new file mode 100644
index 0000000..2bdffeb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.json
@@ -0,0 +1,11 @@ 
+# FIXME: poor message: we require branches to be a complex type name
+# TODO: should we allow anonymous inline types?
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'type': 'Base',
+  'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+{ 'union': 'TestUnion',
+  'base': { 'enum1': 'TestEnum', 'kind': 'str' },
+  'discriminator': 'enum1',
+  'data': { 'value1': { 'string': 'str' },
+            'value2': { 'integer': 'int' } } }
diff --git a/tests/qapi-schema/flat-union-inline.out b/tests/qapi-schema/flat-union-inline.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-int-branch.err b/tests/qapi-schema/flat-union-int-branch.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-int-branch.exit b/tests/qapi-schema/flat-union-int-branch.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/flat-union-int-branch.json b/tests/qapi-schema/flat-union-int-branch.json
new file mode 100644
index 0000000..3543215
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.json
@@ -0,0 +1,12 @@ 
+# FIXME: we should require flat union branches to be a complex type
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'type': 'Base',
+  'data': { 'enum1': 'TestEnum' } }
+{ 'type': 'TestTypeB',
+  'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+  'base': 'Base',
+  'discriminator': 'enum1',
+  'data': { 'value1': 'int',
+            'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-int-branch.out b/tests/qapi-schema/flat-union-int-branch.out
new file mode 100644
index 0000000..cd40e6c
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.out
@@ -0,0 +1,7 @@ 
+[OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
+ OrderedDict([('type', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
+ OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('union', 'TestUnion'), ('base', 'Base'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'int'), ('value2', 'TestTypeB')]))])]
+[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
+[OrderedDict([('type', 'Base'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
+ OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err
index a59749e..97323a0 100644
--- a/tests/qapi-schema/flat-union-no-base.err
+++ b/tests/qapi-schema/flat-union-no-base.err
@@ -1 +1 @@ 
-tests/qapi-schema/flat-union-no-base.json:7: Flat union 'TestUnion' must have a base field
+tests/qapi-schema/flat-union-no-base.json:9: Flat union 'TestUnion' must have a base field
diff --git a/tests/qapi-schema/flat-union-no-base.json b/tests/qapi-schema/flat-union-no-base.json
index 50f2673..08a0247 100644
--- a/tests/qapi-schema/flat-union-no-base.json
+++ b/tests/qapi-schema/flat-union-no-base.json
@@ -1,10 +1,12 @@ 
+# FIXME: flat unions should require a base
+# TODO: simple unions should be able to use an enum discriminator
 { 'type': 'TestTypeA',
   'data': { 'string': 'str' } }
-
 { 'type': 'TestTypeB',
   'data': { 'integer': 'int' } }
-
+{ 'enum': 'Enum',
+  'data': [ 'value1', 'value2' ] }
 { 'union': 'TestUnion',
-  'discriminator': 'enum1',
+  'discriminator': 'Enum',
   'data': { 'value1': 'TestTypeA',
             'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.err b/tests/qapi-schema/flat-union-optional-discriminator.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.exit b/tests/qapi-schema/flat-union-optional-discriminator.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.json b/tests/qapi-schema/flat-union-optional-discriminator.json
new file mode 100644
index 0000000..ece0d31
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.json
@@ -0,0 +1,10 @@ 
+# FIXME: we should require the discriminator to be non-optional
+{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
+{ 'type': 'Base',
+  'data': { '*switch': 'Enum' } }
+{ 'type': 'Branch', 'data': { 'name': 'str' } }
+{ 'union': 'MyUnion',
+  'base': 'Base',
+  'discriminator': '*switch',
+  'data': { 'one': 'Branch',
+            'two': 'Branch' } }
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.out b/tests/qapi-schema/flat-union-optional-discriminator.out
new file mode 100644
index 0000000..bb7db00
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.out
@@ -0,0 +1,7 @@ 
+[OrderedDict([('enum', 'Enum'), ('data', ['one', 'two'])]),
+ OrderedDict([('type', 'Base'), ('data', OrderedDict([('*switch', 'Enum')]))]),
+ OrderedDict([('type', 'Branch'), ('data', OrderedDict([('name', 'str')]))]),
+ OrderedDict([('union', 'MyUnion'), ('base', 'Base'), ('discriminator', '*switch'), ('data', OrderedDict([('one', 'Branch'), ('two', 'Branch')]))])]
+[{'enum_name': 'Enum', 'enum_values': ['one', 'two']}]
+[OrderedDict([('type', 'Base'), ('data', OrderedDict([('*switch', 'Enum')]))]),
+ OrderedDict([('type', 'Branch'), ('data', OrderedDict([('name', 'str')]))])]
diff --git a/tests/qapi-schema/union-bad-branch.err b/tests/qapi-schema/union-bad-branch.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/union-bad-branch.exit b/tests/qapi-schema/union-bad-branch.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/union-bad-branch.json b/tests/qapi-schema/union-bad-branch.json
new file mode 100644
index 0000000..11e46de
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.json
@@ -0,0 +1,8 @@ 
+# FIXME: we should reject normal unions where branches would collide in C
+{ 'type': 'One',
+  'data': { 'string': 'str' } }
+{ 'type': 'Two',
+  'data': { 'number': 'int' } }
+{ 'union': 'MyUnion',
+  'data': { 'one': 'One',
+            'ONE': 'Two' } }
diff --git a/tests/qapi-schema/union-bad-branch.out b/tests/qapi-schema/union-bad-branch.out
new file mode 100644
index 0000000..6baf01b
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.out
@@ -0,0 +1,6 @@ 
+[OrderedDict([('type', 'One'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('type', 'Two'), ('data', OrderedDict([('number', 'int')]))]),
+ OrderedDict([('union', 'MyUnion'), ('data', OrderedDict([('one', 'One'), ('ONE', 'Two')]))])]
+[{'enum_name': 'MyUnionKind', 'enum_values': None}]
+[OrderedDict([('type', 'One'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('type', 'Two'), ('data', OrderedDict([('number', 'int')]))])]
diff --git a/tests/qapi-schema/union-base-no-discriminator.err b/tests/qapi-schema/union-base-no-discriminator.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/union-base-no-discriminator.exit b/tests/qapi-schema/union-base-no-discriminator.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/union-base-no-discriminator.json b/tests/qapi-schema/union-base-no-discriminator.json
new file mode 100644
index 0000000..c8cba3a
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.json
@@ -0,0 +1,14 @@ 
+# FIXME: we should reject simple unions with a base
+{ 'type': 'TestTypeA',
+  'data': { 'string': 'str' } }
+
+{ 'type': 'TestTypeB',
+  'data': { 'integer': 'int' } }
+
+{ 'type': 'Base',
+  'data': { 'string': 'str' } }
+
+{ 'union': 'TestUnion',
+  'base': 'Base',
+  'data': { 'value1': 'TestTypeA',
+            'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/union-base-no-discriminator.out b/tests/qapi-schema/union-base-no-discriminator.out
new file mode 100644
index 0000000..505fd57
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.out
@@ -0,0 +1,8 @@ 
+[OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('type', 'Base'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('union', 'TestUnion'), ('base', 'Base'), ('data', OrderedDict([('value1', 'TestTypeA'), ('value2', 'TestTypeB')]))])]
+[{'enum_name': 'TestUnionKind', 'enum_values': None}]
+[OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('type', 'Base'), ('data', OrderedDict([('string', 'str')]))])]
diff --git a/tests/qapi-schema/union-invalid-base.err b/tests/qapi-schema/union-invalid-base.err
index 938f969..3cc82c0 100644
--- a/tests/qapi-schema/union-invalid-base.err
+++ b/tests/qapi-schema/union-invalid-base.err
@@ -1 +1 @@ 
-tests/qapi-schema/union-invalid-base.json:7: Base 'TestBaseWrong' is not a valid type
+tests/qapi-schema/union-invalid-base.json:8: Base 'int' is not a valid type
diff --git a/tests/qapi-schema/union-invalid-base.json b/tests/qapi-schema/union-invalid-base.json
index 1fa4930..bc5dc8d 100644
--- a/tests/qapi-schema/union-invalid-base.json
+++ b/tests/qapi-schema/union-invalid-base.json
@@ -1,3 +1,4 @@ 
+# a union base type must be a struct
 { 'type': 'TestTypeA',
   'data': { 'string': 'str' } }

@@ -5,6 +6,7 @@ 
   'data': { 'integer': 'int' } }

 { 'union': 'TestUnion',
-  'base': 'TestBaseWrong',
+  'base': 'int',
+  'discriminator': 'int',
   'data': { 'value1': 'TestTypeA',
             'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/union-max.err b/tests/qapi-schema/union-max.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/union-max.exit b/tests/qapi-schema/union-max.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/union-max.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/union-max.json b/tests/qapi-schema/union-max.json
new file mode 100644
index 0000000..45648c4
--- /dev/null
+++ b/tests/qapi-schema/union-max.json
@@ -0,0 +1,3 @@ 
+# FIXME: we should reject 'max' branch in a union, for collision with C enum
+{ 'union': 'Union',
+  'data': { 'max': 'int' } }
diff --git a/tests/qapi-schema/union-max.out b/tests/qapi-schema/union-max.out
new file mode 100644
index 0000000..2757d36
--- /dev/null
+++ b/tests/qapi-schema/union-max.out
@@ -0,0 +1,3 @@ 
+[OrderedDict([('union', 'Union'), ('data', OrderedDict([('max', 'int')]))])]
+[{'enum_name': 'UnionKind', 'enum_values': None}]
+[]
diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/union-optional-branch.exit b/tests/qapi-schema/union-optional-branch.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/union-optional-branch.json b/tests/qapi-schema/union-optional-branch.json
new file mode 100644
index 0000000..c513db7
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.json
@@ -0,0 +1,2 @@ 
+# FIXME: union branches cannot be optional
+{ 'union': 'Union', 'data': { '*a': 'int', 'b': 'str' } }
diff --git a/tests/qapi-schema/union-optional-branch.out b/tests/qapi-schema/union-optional-branch.out
new file mode 100644
index 0000000..b03b5d2
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.out
@@ -0,0 +1,3 @@ 
+[OrderedDict([('union', 'Union'), ('data', OrderedDict([('*a', 'int'), ('b', 'str')]))])]
+[{'enum_name': 'UnionKind', 'enum_values': None}]
+[]
diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/union-unknown.exit b/tests/qapi-schema/union-unknown.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.exit
@@ -0,0 +1 @@ 
+0
diff --git a/tests/qapi-schema/union-unknown.json b/tests/qapi-schema/union-unknown.json
new file mode 100644
index 0000000..258f1d3
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.json
@@ -0,0 +1,3 @@ 
+# FIXME: we should reject a union with unknown type in branch
+{ 'union': 'Union',
+  'data': { 'unknown': 'MissingType' } }
diff --git a/tests/qapi-schema/union-unknown.out b/tests/qapi-schema/union-unknown.out
new file mode 100644
index 0000000..8223dcf
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.out
@@ -0,0 +1,3 @@ 
+[OrderedDict([('union', 'Union'), ('data', OrderedDict([('unknown', 'MissingType')]))])]
+[{'enum_name': 'UnionKind', 'enum_values': None}]
+[]