"Initial commit to Gerrit"
[profile/ivi/gpsd.git] / test_json.c
1 /* json.c - unit test for JSON partsing into fixed-extent structures
2  *
3  * This file is Copyright (c) 2010 by the GPSD project
4  * BSD terms apply: see the file COPYING in the distribution root for details.
5  */
6
7 #include <assert.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stddef.h>
11 #include <string.h>
12
13 #include "gpsd.h"
14 #include "gps_json.h"
15
16 #include "strl.c"
17
18 static void assert_case(int num, int status)
19 {
20     if (status != 0) {
21         (void)fprintf(stderr, "case %d FAILED, status %d (%s).\n", num,
22                       status, json_error_string(status));
23         exit(1);
24     }
25 }
26
27 static void assert_string(char *attr, char *fld, char *val)
28 {
29     if (strcmp(fld, val)) {
30         (void)fprintf(stderr,
31                       "'%s' string attribute eval failed, value = %s.\n",
32                       attr, fld);
33         exit(1);
34     }
35 }
36
37 static void assert_integer(char *attr, int fld, int val)
38 {
39     if (fld != val) {
40         (void)fprintf(stderr,
41                       "'%s' integer attribute eval failed, value = %d.\n",
42                       attr, fld);
43         exit(1);
44     }
45 }
46
47 static void assert_uinteger(char *attr, uint fld, uint val)
48 {
49     if (fld != val) {
50         (void)fprintf(stderr,
51                       "'%s' integer attribute eval failed, value = %u.\n",
52                       attr, fld);
53         exit(1);
54     }
55 }
56
57 static void assert_boolean(char *attr, bool fld, bool val)
58 {
59     /*@-boolcompare@*/
60     if (fld != val) {
61         (void)fprintf(stderr,
62                       "'%s' boolean attribute eval failed, value = %s.\n",
63                       attr, fld ? "true" : "false");
64         exit(1);
65     }
66     /*@+boolcompare@*/
67 }
68
69 /*
70  * Floating point comparisons are iffy, but at least if any of these fail
71  * the output will make it clear whether it was a precision issue
72  */
73 static void assert_real(char *attr, double fld, double val)
74 {
75     if (fld != val) {
76         (void)fprintf(stderr,
77                       "'%s' real attribute eval failed, value = %f.\n", attr,
78                       fld);
79         exit(1);
80     }
81 }
82
83 /*@ -fullinitblock @*/
84
85 static struct gps_data_t gpsdata;
86
87 /* Case 1: TPV report */
88
89 /* *INDENT-OFF* */
90 static const char json_str1[] = "{\"class\":\"TPV\",\
91     \"device\":\"GPS#1\",\"tag\":\"MID2\",                              \
92     \"time\":1119197561.890,\"lon\":46.498203637,\"lat\":7.568074350,\
93     \"alt\":1327.780,\"epx\":21.000,\"epy\":23.000,\"epv\":124.484,\"mode\":3}";
94
95 /* Case 2: SKY report */
96
97 static const char *json_str2 = "{\"class\":\"SKY\",\
98          \"tag\":\"MID4\",\"time\":1119197562.890,   \
99          \"satellites\":[\
100          {\"PRN\":10,\"el\":45,\"az\":196,\"ss\":34,\"used\":true},\
101          {\"PRN\":29,\"el\":67,\"az\":310,\"ss\":40,\"used\":true},\
102          {\"PRN\":28,\"el\":59,\"az\":108,\"ss\":42,\"used\":true},\
103          {\"PRN\":26,\"el\":51,\"az\":304,\"ss\":43,\"used\":true},\
104          {\"PRN\":8,\"el\":44,\"az\":58,\"ss\":41,\"used\":true},\
105          {\"PRN\":27,\"el\":16,\"az\":66,\"ss\":39,\"used\":true},\
106          {\"PRN\":21,\"el\":10,\"az\":301,\"ss\":0,\"used\":false}]}";
107
108 /* Case 3: String list syntax */
109
110 static const char *json_str3 = "[\"foo\",\"bar\",\"baz\"]";
111
112 static char *stringptrs[3];
113 static char stringstore[256];
114 static int stringcount;
115
116 /*@-type@*/
117 static const struct json_array_t json_array_3 = {
118     .element_type = t_string,
119     .arr.strings.ptrs = stringptrs,
120     .arr.strings.store = stringstore,
121     .arr.strings.storelen = sizeof(stringstore),
122     .count = &stringcount,
123     .maxlen = sizeof(stringptrs)/sizeof(stringptrs[0]),
124 };
125 /*@+type@*/
126
127 /* Case 4: test defaulting of unspecified attributes */
128
129 static const char *json_str4 = "{\"flag1\":true,\"flag2\":false}";
130
131 static bool flag1, flag2;
132 static double dftreal;
133 static int dftinteger;
134 static unsigned int dftuinteger;
135
136 static const struct json_attr_t json_attrs_4[] = {
137     {"dftint",  t_integer, .addr.integer = &dftinteger, .dflt.integer = -5},
138     {"dftuint", t_integer, .addr.uinteger = &dftuinteger, .dflt.uinteger = 10},
139     {"dftreal", t_real,    .addr.real = &dftreal,       .dflt.real = 23.17},
140     {"flag1",   t_boolean, .addr.boolean = &flag1,},
141     {"flag2",   t_boolean, .addr.boolean = &flag2,},
142     {NULL},
143 };
144
145 /* Case 5: test DEVICE parsing */
146
147 static const char *json_str5 = "{\"class\":\"DEVICE\",\
148            \"path\":\"/dev/ttyUSB0\",\
149            \"flags\":5,\
150            \"driver\":\"Foonly\",\"subtype\":\"Foonly Frob\"\
151            }";
152
153 /* Case 6: test parsing of subobject list into array of structures */
154
155 static const char *json_str6 = "{\"parts\":[\
156            {\"name\":\"Urgle\", \"flag\":true, \"count\":3},\
157            {\"name\":\"Burgle\",\"flag\":false,\"count\":1},\
158            {\"name\":\"Witter\",\"flag\":true, \"count\":4},\
159            {\"name\":\"Thud\",  \"flag\":false,\"count\":1}]}";
160
161 struct dumbstruct_t {
162     char name[64];
163     bool flag;
164     int count;
165 };
166 static struct dumbstruct_t dumbstruck[5];
167 static int dumbcount;
168
169 /*@-type@*/
170 static const struct json_attr_t json_attrs_6_subtype[] = {
171     {"name",  t_string,  .addr.offset = offsetof(struct dumbstruct_t, name),
172                          .len = 64},
173     {"flag",  t_boolean, .addr.offset = offsetof(struct dumbstruct_t, flag),},
174     {"count", t_integer, .addr.offset = offsetof(struct dumbstruct_t, count),},
175     {NULL},
176 };
177
178 static const struct json_attr_t json_attrs_6[] = {
179     {"parts", t_array, .addr.array.element_type = t_structobject,
180                        .addr.array.arr.objects.base = (char*)&dumbstruck,
181                        .addr.array.arr.objects.stride = sizeof(struct dumbstruct_t),
182                        .addr.array.arr.objects.subtype = json_attrs_6_subtype,
183                        .addr.array.count = &dumbcount,
184                        .addr.array.maxlen = sizeof(dumbstruck)/sizeof(dumbstruck[0])},
185     {NULL},
186 };
187 /*@+type@*/
188
189 /* Case 7: test parsing of version response */
190
191 static const char *json_str7 = "{\"class\":\"VERSION\",\
192            \"release\":\"2.40dev\",\"rev\":\"dummy-revision\",\
193            \"proto_major\":3,\"proto_minor\":1}";
194
195 /* Case 8: test parsing arrays of enumerated types */
196
197 static const char *json_str8 = "{\"fee\":\"FOO\",\"fie\":\"BAR\",\"foe\":\"BAZ\"}";
198 static const struct json_enum_t enum_table[] = {
199     {"BAR", 6}, {"FOO", 3}, {"BAZ", 14}, {NULL}
200 };
201
202 static int fee, fie, foe;
203 static const struct json_attr_t json_attrs_8[] = {
204     {"fee",  t_integer, .addr.integer = &fee, .map=enum_table},
205     {"fie",  t_integer, .addr.integer = &fie, .map=enum_table},
206     {"foe",  t_integer, .addr.integer = &foe, .map=enum_table},
207     {NULL},
208 };
209 /*@ +fullinitblock @*/
210 /* *INDENT-ON* */
211
212 int main(int argc UNUSED, char *argv[]UNUSED)
213 {
214     int status = 0;
215
216     (void)fprintf(stderr, "JSON unit test ");
217
218     status = libgps_json_unpack(json_str1, &gpsdata, NULL);
219     assert_case(1, status);
220     assert_string("device", gpsdata.dev.path, "GPS#1");
221     assert_string("tag", gpsdata.tag, "MID2");
222     assert_integer("mode", gpsdata.fix.mode, 3);
223     assert_real("time", gpsdata.fix.time, 1119197561.890);
224     assert_real("lon", gpsdata.fix.longitude, 46.498203637);
225     assert_real("lat", gpsdata.fix.latitude, 7.568074350);
226
227     status = libgps_json_unpack(json_str2, &gpsdata, NULL);
228     assert_case(2, status);
229     assert_string("tag", gpsdata.tag, "MID4");
230     assert_integer("used", gpsdata.satellites_used, 6);
231     assert_integer("PRN[0]", gpsdata.PRN[0], 10);
232     assert_integer("el[0]", gpsdata.elevation[0], 45);
233     assert_integer("az[0]", gpsdata.azimuth[0], 196);
234     assert_real("ss[0]", gpsdata.ss[0], 34);
235     assert_integer("used[0]", gpsdata.used[0], 10);
236     assert_integer("used[5]", gpsdata.used[5], 27);
237     assert_integer("PRN[6]", gpsdata.PRN[6], 21);
238     assert_integer("el[6]", gpsdata.elevation[6], 10);
239     assert_integer("az[6]", gpsdata.azimuth[6], 301);
240     assert_real("ss[6]", gpsdata.ss[6], 0);
241
242     status = json_read_array(json_str3, &json_array_3, NULL);
243     assert_case(3, status);
244     assert(stringcount == 3);
245     assert(strcmp(stringptrs[0], "foo") == 0);
246     assert(strcmp(stringptrs[1], "bar") == 0);
247     assert(strcmp(stringptrs[2], "baz") == 0);
248
249     status = json_read_object(json_str4, json_attrs_4, NULL);
250     assert_case(4, status);
251     assert_integer("dftint", dftinteger, -5);   /* did the default work? */
252     assert_uinteger("dftuint", dftuinteger, 10);        /* did the default work? */
253     assert_real("dftreal", dftreal, 23.17);     /* did the default work? */
254     assert_boolean("flag1", flag1, true);
255     assert_boolean("flag2", flag2, false);
256
257     status = libgps_json_unpack(json_str5, &gpsdata, NULL);
258     assert_case(5, status);
259     assert_string("path", gpsdata.dev.path, "/dev/ttyUSB0");
260     assert_integer("flags", gpsdata.dev.flags, 5);
261     assert_string("driver", gpsdata.dev.driver, "Foonly");
262
263     status = json_read_object(json_str6, json_attrs_6, NULL);
264     assert_case(6, status);
265     assert_integer("dumbcount", dumbcount, 4);
266     assert_string("dumbstruck[0].name", dumbstruck[0].name, "Urgle");
267     assert_string("dumbstruck[1].name", dumbstruck[1].name, "Burgle");
268     assert_string("dumbstruck[2].name", dumbstruck[2].name, "Witter");
269     assert_string("dumbstruck[3].name", dumbstruck[3].name, "Thud");
270     assert_boolean("dumbstruck[0].flag", dumbstruck[0].flag, true);
271     assert_boolean("dumbstruck[1].flag", dumbstruck[1].flag, false);
272     assert_boolean("dumbstruck[2].flag", dumbstruck[2].flag, true);
273     assert_boolean("dumbstruck[3].flag", dumbstruck[3].flag, false);
274     assert_integer("dumbstruck[0].count", dumbstruck[0].count, 3);
275     assert_integer("dumbstruck[1].count", dumbstruck[1].count, 1);
276     assert_integer("dumbstruck[2].count", dumbstruck[2].count, 4);
277     assert_integer("dumbstruck[3].count", dumbstruck[3].count, 1);
278
279     status = libgps_json_unpack(json_str7, &gpsdata, NULL);
280     assert_case(7, status);
281     assert_string("release", gpsdata.version.release, "2.40dev");
282     assert_string("rev", gpsdata.version.rev, "dummy-revision");
283     assert_integer("proto_major", gpsdata.version.proto_major, 3);
284     assert_integer("proto_minor", gpsdata.version.proto_minor, 1);
285
286     status = json_read_object(json_str8, json_attrs_8, NULL);
287     assert_case(8, status);
288     assert_integer("fee", fee, 3);
289     assert_integer("fie", fie, 6);
290     assert_integer("foe", foe, 14);
291
292     (void)fprintf(stderr, "succeeded.\n");
293
294     exit(0);
295 }