qemu/tests/test-qdist.c
<<
>>
Prefs
   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
  12struct 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 */
  21static 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
  34static 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
  52static void
  53histogram_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
  64static 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
  71static void
  72entries_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
  83static void
  84entries_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
  93static 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
 125static 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
 202static inline void do_test_pr_label(uint32_t opt)
 203{
 204    opt |= QDIST_PR_LABELS;
 205    do_test_pr(opt);
 206}
 207
 208static 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
 224static 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
 245static 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
 273static 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
 287static 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
 305static 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
 323static 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
 351static 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
 377int 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