Skip to content

Commit a6d433d

Browse files
committed
internal/qmp-gen: initial TestGenerate with StatusInfo type
1 parent c6616b2 commit a6d433d

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed

internal/qmp-gen/parse_test.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,163 @@
1515
package gen
1616

1717
import (
18+
"bytes"
19+
"go/format"
20+
"net/http"
21+
"net/http/httptest"
1822
"strings"
1923
"testing"
2024
)
2125

26+
func TestGenerate(t *testing.T) {
27+
tests := []struct {
28+
name string
29+
in []byte
30+
out []byte
31+
}{
32+
{
33+
name: "StructInfo",
34+
in: []byte(`
35+
##
36+
# @StatusInfo:
37+
#
38+
# Information about VCPU run state
39+
#
40+
# @running: true if all VCPUs are runnable, false if not runnable
41+
#
42+
# @singlestep: true if VCPUs are in single-step mode
43+
#
44+
# @status: the virtual machine @RunState
45+
#
46+
# Since: 0.14.0
47+
#
48+
# Notes: @singlestep is enabled through the GDB stub
49+
##
50+
{ 'struct': 'StatusInfo',
51+
'data': {'running': 'bool', 'singlestep': 'bool', 'status': 'RunState'} }
52+
`),
53+
out: []byte(`
54+
// StatusInfo -> StatusInfo (struct)
55+
56+
// StatusInfo implements the "StatusInfo" QMP API type.
57+
type StatusInfo struct {
58+
Running bool 'json:"running"'
59+
Singlestep bool 'json:"singlestep"'
60+
Status RunState 'json:"status"'
61+
}
62+
`),
63+
},
64+
}
65+
66+
for _, tt := range tests {
67+
t.Run(tt.name, func(t *testing.T) {
68+
// Replace single quote with backtick to match generated Go
69+
// source code
70+
tt.out = bytes.TrimSpace(bytes.Replace(tt.out, []byte("'"), []byte("`"), -1))
71+
72+
// Do not check needed types, third parameter is false
73+
source := testGenerate(t, tt.in, false)
74+
75+
if want, got := tt.out, source; !bytes.Contains(got, want) {
76+
t.Fatalf("generated code does not match actual code:\n- want: %s\n- got: %s",
77+
indent(string(want)), indent(string(got)))
78+
}
79+
})
80+
}
81+
}
82+
83+
func TestGenerateNeededTypes(t *testing.T) {
84+
tests := []struct {
85+
name string
86+
in []byte
87+
out []byte
88+
}{
89+
{
90+
name: "no output: no command that uses StructInfo",
91+
in: []byte(`
92+
##
93+
# @StatusInfo:
94+
#
95+
# Information about VCPU run state
96+
#
97+
# @running: true if all VCPUs are runnable, false if not runnable
98+
#
99+
# @singlestep: true if VCPUs are in single-step mode
100+
#
101+
# @status: the virtual machine @RunState
102+
#
103+
# Since: 0.14.0
104+
#
105+
# Notes: @singlestep is enabled through the GDB stub
106+
##
107+
{ 'struct': 'StatusInfo',
108+
'data': {'running': 'bool', 'singlestep': 'bool', 'status': 'RunState'} }
109+
`),
110+
},
111+
{
112+
name: "StatusInfo with query-status command",
113+
in: []byte(`
114+
##
115+
# @StatusInfo:
116+
#
117+
# Information about VCPU run state
118+
#
119+
# @running: true if all VCPUs are runnable, false if not runnable
120+
#
121+
# @singlestep: true if VCPUs are in single-step mode
122+
#
123+
# @status: the virtual machine @RunState
124+
#
125+
# Since: 0.14.0
126+
#
127+
# Notes: @singlestep is enabled through the GDB stub
128+
##
129+
{ 'struct': 'StatusInfo',
130+
'data': {'running': 'bool', 'singlestep': 'bool', 'status': 'RunState'} }
131+
132+
##
133+
# @query-status:
134+
#
135+
# Query the run status of all VCPUs
136+
#
137+
# Returns: @StatusInfo reflecting all VCPUs
138+
#
139+
# Since: 0.14.0
140+
##
141+
{ 'command': 'query-status', 'returns': 'StatusInfo' }
142+
`),
143+
out: []byte(`
144+
// StatusInfo -> StatusInfo (struct)
145+
146+
// StatusInfo implements the "StatusInfo" QMP API type.
147+
type StatusInfo struct {
148+
Running bool 'json:"running"'
149+
Singlestep bool 'json:"singlestep"'
150+
Status RunState 'json:"status"'
151+
}
152+
153+
// COMMAND query-status
154+
`),
155+
},
156+
}
157+
158+
for _, tt := range tests {
159+
t.Run(tt.name, func(t *testing.T) {
160+
// Replace single quote with backtick to match generated Go
161+
// source code
162+
tt.out = bytes.TrimSpace(bytes.Replace(tt.out, []byte("'"), []byte("`"), -1))
163+
164+
// Check needed types, third parameter is true
165+
source := testGenerate(t, tt.in, true)
166+
167+
if want, got := tt.out, source; !bytes.Contains(got, want) {
168+
t.Fatalf("generated code does not match actual code:\n- want: %s\n- got: %s",
169+
indent(string(want)), indent(string(got)))
170+
}
171+
})
172+
}
173+
}
174+
22175
func indent(s string) string {
23176
return strings.Replace(s, "\n", "\n ", -1)
24177
}
@@ -79,3 +232,37 @@ Got:
79232
})
80233
}
81234
}
235+
236+
func testGenerate(t *testing.T, in []byte, checkNeededTypes bool) []byte {
237+
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
238+
_, _ = w.Write(in)
239+
}))
240+
defer s.Close()
241+
242+
defs, err := readDefinitions(s.URL)
243+
if err != nil {
244+
t.Fatalf("unexpected error: %v", err)
245+
}
246+
247+
symbols, err := parse(defs)
248+
if err != nil {
249+
t.Fatalf("unexpected error: %v", err)
250+
}
251+
252+
need := symbols
253+
if checkNeededTypes {
254+
need = neededTypes(symbols)
255+
}
256+
257+
bs, err := renderAPI("templates/", symbols, need)
258+
if err != nil {
259+
t.Fatalf("unexpected error: %v", err)
260+
}
261+
262+
formatted, err := format.Source(bs)
263+
if err != nil {
264+
t.Fatalf("unexpected error: %v", err)
265+
}
266+
267+
return formatted
268+
}

0 commit comments

Comments
 (0)