xref: /qemu/tests/unit/test-qdist.c (revision 727385c4)
1 /*
2  * Copyright (C) 2016, Emilio G. Cota <cota@braap.org>
3  *
4  * License: GNU GPL, version 2 or later.
5  *   See the COPYING file in the top-level directory.
6  */
7 #include "qemu/osdep.h"
8 #include "qemu/qdist.h"
9 
10 #include <math.h>
11 
12 struct entry_desc {
13     double x;
14     unsigned long count;
15 
16     /* 0 prints a space, 1-8 prints from qdist_blocks[] */
17     int fill_code;
18 };
19 
20 /* See: https://en.wikipedia.org/wiki/Block_Elements */
21 static const gunichar qdist_blocks[] = {
22     0x2581,
23     0x2582,
24     0x2583,
25     0x2584,
26     0x2585,
27     0x2586,
28     0x2587,
29     0x2588
30 };
31 
32 #define QDIST_NR_BLOCK_CODES ARRAY_SIZE(qdist_blocks)
33 
34 static char *pr_hist(const struct entry_desc *darr, size_t n)
35 {
36     GString *s = g_string_new("");
37     size_t i;
38 
39     for (i = 0; i < n; i++) {
40         int fill = darr[i].fill_code;
41 
42         if (fill) {
43             assert(fill <= QDIST_NR_BLOCK_CODES);
44             g_string_append_unichar(s, qdist_blocks[fill - 1]);
45         } else {
46             g_string_append_c(s, ' ');
47         }
48     }
49     return g_string_free(s, FALSE);
50 }
51 
52 static void
53 histogram_check(const struct qdist *dist, const struct entry_desc *darr,
54                 size_t n, size_t n_bins)
55 {
56     char *pr = qdist_pr_plain(dist, n_bins);
57     char *str = pr_hist(darr, n);
58 
59     g_assert_cmpstr(pr, ==, str);
60     g_free(pr);
61     g_free(str);
62 }
63 
64 static void histogram_check_single_full(const struct qdist *dist, size_t n_bins)
65 {
66     struct entry_desc desc = { .fill_code = 8 };
67 
68     histogram_check(dist, &desc, 1, n_bins);
69 }
70 
71 static void
72 entries_check(const struct qdist *dist, const struct entry_desc *darr, size_t n)
73 {
74     size_t i;
75 
76     for (i = 0; i < n; i++) {
77         struct qdist_entry *e = &dist->entries[i];
78 
79         g_assert_cmpuint(e->count, ==, darr[i].count);
80     }
81 }
82 
83 static void
84 entries_insert(struct qdist *dist, const struct entry_desc *darr, size_t n)
85 {
86     size_t i;
87 
88     for (i = 0; i < n; i++) {
89         qdist_add(dist, darr[i].x, darr[i].count);
90     }
91 }
92 
93 static void do_test_bin(const struct entry_desc *a, size_t n_a,
94                         const struct entry_desc *b, size_t n_b)
95 {
96     struct qdist qda;
97     struct qdist qdb;
98 
99     qdist_init(&qda);
100 
101     entries_insert(&qda, a, n_a);
102     qdist_inc(&qda, a[0].x);
103     qdist_add(&qda, a[0].x, -1);
104 
105     g_assert_cmpuint(qdist_unique_entries(&qda), ==, n_a);
106     g_assert_cmpfloat(qdist_xmin(&qda), ==, a[0].x);
107     g_assert_cmpfloat(qdist_xmax(&qda), ==, a[n_a - 1].x);
108     histogram_check(&qda, a, n_a, 0);
109     histogram_check(&qda, a, n_a, n_a);
110 
111     qdist_bin__internal(&qdb, &qda, n_b);
112     g_assert_cmpuint(qdb.n, ==, n_b);
113     entries_check(&qdb, b, n_b);
114     g_assert_cmpuint(qdist_sample_count(&qda), ==, qdist_sample_count(&qdb));
115     /*
116      * No histogram_check() for $qdb, since we'd rebin it and that is a bug.
117      * Instead, regenerate it from $qda.
118      */
119     histogram_check(&qda, b, n_b, n_b);
120 
121     qdist_destroy(&qdb);
122     qdist_destroy(&qda);
123 }
124 
125 static void do_test_pr(uint32_t opt)
126 {
127     static const struct entry_desc desc[] = {
128         [0] = { 1, 900, 8 },
129         [1] = { 2, 1, 1 },
130         [2] = { 3, 2, 1 }
131     };
132     static const char border[] = "|";
133     const char *llabel = NULL;
134     const char *rlabel = NULL;
135     struct qdist dist;
136     GString *s;
137     char *str;
138     char *pr;
139     size_t n;
140 
141     n = ARRAY_SIZE(desc);
142     qdist_init(&dist);
143 
144     entries_insert(&dist, desc, n);
145     histogram_check(&dist, desc, n, 0);
146 
147     s = g_string_new("");
148 
149     if (opt & QDIST_PR_LABELS) {
150         unsigned int lopts = opt & (QDIST_PR_NODECIMAL |
151                                     QDIST_PR_PERCENT |
152                                     QDIST_PR_100X |
153                                     QDIST_PR_NOBINRANGE);
154 
155         if (lopts == 0) {
156             llabel = "[1.0,1.7)";
157             rlabel = "[2.3,3.0]";
158         } else if (lopts == QDIST_PR_NODECIMAL) {
159             llabel = "[1,2)";
160             rlabel = "[2,3]";
161         } else if (lopts == (QDIST_PR_PERCENT | QDIST_PR_NODECIMAL)) {
162             llabel = "[1,2)%";
163             rlabel = "[2,3]%";
164         } else if (lopts == QDIST_PR_100X) {
165             llabel = "[100.0,166.7)";
166             rlabel = "[233.3,300.0]";
167         } else if (lopts == (QDIST_PR_NOBINRANGE | QDIST_PR_NODECIMAL)) {
168             llabel = "1";
169             rlabel = "3";
170         } else {
171             g_assert_cmpstr("BUG", ==, "This is not meant to be exhaustive");
172         }
173     }
174 
175     if (llabel) {
176         g_string_append(s, llabel);
177     }
178     if (opt & QDIST_PR_BORDER) {
179         g_string_append(s, border);
180     }
181 
182     str = pr_hist(desc, n);
183     g_string_append(s, str);
184     g_free(str);
185 
186     if (opt & QDIST_PR_BORDER) {
187         g_string_append(s, border);
188     }
189     if (rlabel) {
190         g_string_append(s, rlabel);
191     }
192 
193     str = g_string_free(s, FALSE);
194     pr = qdist_pr(&dist, n, opt);
195     g_assert_cmpstr(pr, ==, str);
196     g_free(pr);
197     g_free(str);
198 
199     qdist_destroy(&dist);
200 }
201 
202 static inline void do_test_pr_label(uint32_t opt)
203 {
204     opt |= QDIST_PR_LABELS;
205     do_test_pr(opt);
206 }
207 
208 static void test_pr(void)
209 {
210     do_test_pr(0);
211 
212     do_test_pr(QDIST_PR_BORDER);
213 
214     /* 100X should be ignored because we're not setting LABELS */
215     do_test_pr(QDIST_PR_100X);
216 
217     do_test_pr_label(0);
218     do_test_pr_label(QDIST_PR_NODECIMAL);
219     do_test_pr_label(QDIST_PR_PERCENT | QDIST_PR_NODECIMAL);
220     do_test_pr_label(QDIST_PR_100X);
221     do_test_pr_label(QDIST_PR_NOBINRANGE | QDIST_PR_NODECIMAL);
222 }
223 
224 static void test_bin_shrink(void)
225 {
226     static const struct entry_desc a[] = {
227         [0] = { 0.0,   42922, 7 },
228         [1] = { 0.25,  47834, 8 },
229         [2] = { 0.50,  26628, 0 },
230         [3] = { 0.625, 597,   4 },
231         [4] = { 0.75,  10298, 1 },
232         [5] = { 0.875, 22,    2 },
233         [6] = { 1.0,   2771,  1 }
234     };
235     static const struct entry_desc b[] = {
236         [0] = { 0.0, 42922, 7 },
237         [1] = { 0.25, 47834, 8 },
238         [2] = { 0.50, 27225, 3 },
239         [3] = { 0.75, 13091, 1 }
240     };
241 
242     return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b));
243 }
244 
245 static void test_bin_expand(void)
246 {
247     static const struct entry_desc a[] = {
248         [0] = { 0.0,   11713, 5 },
249         [1] = { 0.25,  20294, 0 },
250         [2] = { 0.50,  17266, 8 },
251         [3] = { 0.625, 1506,  0 },
252         [4] = { 0.75,  10355, 6 },
253         [5] = { 0.833, 2,     1 },
254         [6] = { 0.875, 99,    4 },
255         [7] = { 1.0,   4301,  2 }
256     };
257     static const struct entry_desc b[] = {
258         [0] = { 0.0, 11713, 5 },
259         [1] = { 0.0, 0,     0 },
260         [2] = { 0.0, 20294, 8 },
261         [3] = { 0.0, 0,     0 },
262         [4] = { 0.0, 0,     0 },
263         [5] = { 0.0, 17266, 6 },
264         [6] = { 0.0, 1506,  1 },
265         [7] = { 0.0, 10355, 4 },
266         [8] = { 0.0, 101,   1 },
267         [9] = { 0.0, 4301,  2 }
268     };
269 
270     return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b));
271 }
272 
273 static void test_bin_precision(void)
274 {
275     static const struct entry_desc a[] = {
276         [0] = { 0, 213549, 8 },
277         [1] = { 1, 70, 1 },
278     };
279     static const struct entry_desc b[] = {
280         [0] = { 0, 213549, 8 },
281         [1] = { 0, 70, 1 },
282     };
283 
284     return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b));
285 }
286 
287 static void test_bin_simple(void)
288 {
289     static const struct entry_desc a[] = {
290         [0] = { 10, 101, 8 },
291         [1] = { 11, 0, 0 },
292         [2] = { 12, 2, 1 }
293     };
294     static const struct entry_desc b[] = {
295         [0] = { 0, 101, 8 },
296         [1] = { 0, 0, 0 },
297         [2] = { 0, 0, 0 },
298         [3] = { 0, 0, 0 },
299         [4] = { 0, 2, 1 }
300     };
301 
302     return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b));
303 }
304 
305 static void test_single_full(void)
306 {
307     struct qdist dist;
308 
309     qdist_init(&dist);
310 
311     qdist_add(&dist, 3, 102);
312     g_assert_cmpfloat(qdist_avg(&dist), ==, 3);
313     g_assert_cmpfloat(qdist_xmin(&dist), ==, 3);
314     g_assert_cmpfloat(qdist_xmax(&dist), ==, 3);
315 
316     histogram_check_single_full(&dist, 0);
317     histogram_check_single_full(&dist, 1);
318     histogram_check_single_full(&dist, 10);
319 
320     qdist_destroy(&dist);
321 }
322 
323 static void test_single_empty(void)
324 {
325     struct qdist dist;
326     char *pr;
327 
328     qdist_init(&dist);
329 
330     qdist_add(&dist, 3, 0);
331     g_assert_cmpuint(qdist_sample_count(&dist), ==, 0);
332     g_assert(isnan(qdist_avg(&dist)));
333     g_assert_cmpfloat(qdist_xmin(&dist), ==, 3);
334     g_assert_cmpfloat(qdist_xmax(&dist), ==, 3);
335 
336     pr = qdist_pr_plain(&dist, 0);
337     g_assert_cmpstr(pr, ==, " ");
338     g_free(pr);
339 
340     pr = qdist_pr_plain(&dist, 1);
341     g_assert_cmpstr(pr, ==, " ");
342     g_free(pr);
343 
344     pr = qdist_pr_plain(&dist, 2);
345     g_assert_cmpstr(pr, ==, " ");
346     g_free(pr);
347 
348     qdist_destroy(&dist);
349 }
350 
351 static void test_none(void)
352 {
353     struct qdist dist;
354     char *pr;
355 
356     qdist_init(&dist);
357 
358     g_assert(isnan(qdist_avg(&dist)));
359     g_assert(isnan(qdist_xmin(&dist)));
360     g_assert(isnan(qdist_xmax(&dist)));
361 
362     pr = qdist_pr_plain(&dist, 0);
363     g_assert_cmpstr(pr, ==, "(empty)");
364     g_free(pr);
365 
366     pr = qdist_pr_plain(&dist, 2);
367     g_assert_cmpstr(pr, ==, "(empty)");
368     g_free(pr);
369 
370     pr = qdist_pr(&dist, 0, QDIST_PR_BORDER);
371     g_assert_cmpstr(pr, ==, "(empty)");
372     g_free(pr);
373 
374     qdist_destroy(&dist);
375 }
376 
377 int main(int argc, char *argv[])
378 {
379     g_test_init(&argc, &argv, NULL);
380     g_test_add_func("/qdist/none", test_none);
381     g_test_add_func("/qdist/single/empty", test_single_empty);
382     g_test_add_func("/qdist/single/full", test_single_full);
383     g_test_add_func("/qdist/binning/simple", test_bin_simple);
384     g_test_add_func("/qdist/binning/precision", test_bin_precision);
385     g_test_add_func("/qdist/binning/expand", test_bin_expand);
386     g_test_add_func("/qdist/binning/shrink", test_bin_shrink);
387     g_test_add_func("/qdist/pr", test_pr);
388     return g_test_run();
389 }
390