On 25/05/16 04:13, Emilio G. Cota wrote: > Reviewed-by: Richard Henderson <r...@twiddle.net> > Signed-off-by: Emilio G. Cota <c...@braap.org>
Acked-by: Sergey Fedorov <sergey.fedo...@linaro.org> > --- > tests/.gitignore | 1 + > tests/Makefile | 6 +- > tests/test-qdist.c | 369 > +++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 375 insertions(+), 1 deletion(-) > create mode 100644 tests/test-qdist.c > > diff --git a/tests/.gitignore b/tests/.gitignore > index a06a8ba..7c0d156 100644 > --- a/tests/.gitignore > +++ b/tests/.gitignore > @@ -48,6 +48,7 @@ test-qapi-types.[ch] > test-qapi-visit.[ch] > test-qdev-global-props > test-qemu-opts > +test-qdist > test-qga > test-qmp-commands > test-qmp-commands.h > diff --git a/tests/Makefile b/tests/Makefile > index 9dddde6..a5af20b 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -70,6 +70,8 @@ check-unit-y += tests/rcutorture$(EXESUF) > gcov-files-rcutorture-y = util/rcu.c > check-unit-y += tests/test-rcu-list$(EXESUF) > gcov-files-test-rcu-list-y = util/rcu.c > +check-unit-y += tests/test-qdist$(EXESUF) > +gcov-files-test-qdist-y = util/qdist.c > check-unit-y += tests/test-bitops$(EXESUF) > check-unit-$(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) += > tests/test-qdev-global-props$(EXESUF) > check-unit-y += tests/check-qom-interface$(EXESUF) > @@ -392,7 +394,8 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o > tests/check-qdict.o \ > tests/test-qmp-commands.o tests/test-visitor-serialization.o \ > tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ > tests/test-opts-visitor.o tests/test-qmp-event.o \ > - tests/rcutorture.o tests/test-rcu-list.o > + tests/rcutorture.o tests/test-rcu-list.o \ > + tests/test-qdist.o > > $(test-obj-y): QEMU_INCLUDES += -Itests > QEMU_CFLAGS += -I$(SRC_PATH)/tests > @@ -431,6 +434,7 @@ tests/test-cutils$(EXESUF): tests/test-cutils.o > util/cutils.o > tests/test-int128$(EXESUF): tests/test-int128.o > tests/rcutorture$(EXESUF): tests/rcutorture.o $(test-util-obj-y) > tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o $(test-util-obj-y) > +tests/test-qdist$(EXESUF): tests/test-qdist.o $(test-util-obj-y) > > tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ > hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\ > diff --git a/tests/test-qdist.c b/tests/test-qdist.c > new file mode 100644 > index 0000000..7625a57 > --- /dev/null > +++ b/tests/test-qdist.c > @@ -0,0 +1,369 @@ > +/* > + * Copyright (C) 2016, Emilio G. Cota <c...@braap.org> > + * > + * License: GNU GPL, version 2 or later. > + * See the COPYING file in the top-level directory. > + */ > +#include "qemu/osdep.h" > +#include <glib.h> > +#include "qemu/qdist.h" > + > +#include <math.h> > + > +struct entry_desc { > + double x; > + unsigned long count; > + > + /* 0 prints a space, 1-8 prints from qdist_blocks[] */ > + int fill_code; > +}; > + > +/* See: https://en.wikipedia.org/wiki/Block_Elements */ > +static const gunichar qdist_blocks[] = { > + 0x2581, > + 0x2582, > + 0x2583, > + 0x2584, > + 0x2585, > + 0x2586, > + 0x2587, > + 0x2588 > +}; > + > +#define QDIST_NR_BLOCK_CODES ARRAY_SIZE(qdist_blocks) > + > +static char *pr_hist(const struct entry_desc *darr, size_t n) > +{ > + GString *s = g_string_new(""); > + size_t i; > + > + for (i = 0; i < n; i++) { > + int fill = darr[i].fill_code; > + > + if (fill) { > + assert(fill <= QDIST_NR_BLOCK_CODES); > + g_string_append_unichar(s, qdist_blocks[fill - 1]); > + } else { > + g_string_append_c(s, ' '); > + } > + } > + return g_string_free(s, FALSE); > +} > + > +static void > +histogram_check(const struct qdist *dist, const struct entry_desc *darr, > + size_t n, size_t n_bins) > +{ > + char *pr = qdist_pr_plain(dist, n_bins); > + char *str = pr_hist(darr, n); > + > + g_assert_cmpstr(pr, ==, str); > + g_free(pr); > + g_free(str); > +} > + > +static void histogram_check_single_full(const struct qdist *dist, size_t > n_bins) > +{ > + struct entry_desc desc = { .fill_code = 8 }; > + > + histogram_check(dist, &desc, 1, n_bins); > +} > + > +static void > +entries_check(const struct qdist *dist, const struct entry_desc *darr, > size_t n) > +{ > + size_t i; > + > + for (i = 0; i < n; i++) { > + struct qdist_entry *e = &dist->entries[i]; > + > + g_assert_cmpuint(e->count, ==, darr[i].count); > + } > +} > + > +static void > +entries_insert(struct qdist *dist, const struct entry_desc *darr, size_t n) > +{ > + size_t i; > + > + for (i = 0; i < n; i++) { > + qdist_add(dist, darr[i].x, darr[i].count); > + } > +} > + > +static void do_test_bin(const struct entry_desc *a, size_t n_a, > + const struct entry_desc *b, size_t n_b) > +{ > + struct qdist qda; > + struct qdist qdb; > + > + qdist_init(&qda); > + > + entries_insert(&qda, a, n_a); > + qdist_inc(&qda, a[0].x); > + qdist_add(&qda, a[0].x, -1); > + > + g_assert_cmpuint(qdist_unique_entries(&qda), ==, n_a); > + g_assert_cmpfloat(qdist_xmin(&qda), ==, a[0].x); > + g_assert_cmpfloat(qdist_xmax(&qda), ==, a[n_a - 1].x); > + histogram_check(&qda, a, n_a, 0); > + histogram_check(&qda, a, n_a, n_a); > + > + qdist_bin__internal(&qdb, &qda, n_b); > + g_assert_cmpuint(qdb.n, ==, n_b); > + entries_check(&qdb, b, n_b); > + g_assert_cmpuint(qdist_sample_count(&qda), ==, qdist_sample_count(&qdb)); > + /* > + * No histogram_check() for $qdb, since we'd rebin it and that is a bug. > + * Instead, regenerate it from $qda. > + */ > + histogram_check(&qda, b, n_b, n_b); > + > + qdist_destroy(&qdb); > + qdist_destroy(&qda); > +} > + > +static void do_test_pr(uint32_t opt) > +{ > + static const struct entry_desc desc[] = { > + [0] = { 1, 900, 8 }, > + [1] = { 2, 1, 1 }, > + [2] = { 3, 2, 1 } > + }; > + static const char border[] = "|"; > + const char *llabel = NULL; > + const char *rlabel = NULL; > + struct qdist dist; > + GString *s; > + char *str; > + char *pr; > + size_t n; > + > + n = ARRAY_SIZE(desc); > + qdist_init(&dist); > + > + entries_insert(&dist, desc, n); > + histogram_check(&dist, desc, n, 0); > + > + s = g_string_new(""); > + > + if (opt & QDIST_PR_LABELS) { > + unsigned int lopts = opt & (QDIST_PR_NODECIMAL | > + QDIST_PR_PERCENT | > + QDIST_PR_100X | > + QDIST_PR_NOBINRANGE); > + > + if (lopts == 0) { > + llabel = "[1.0,1.7)"; > + rlabel = "[2.3,3.0]"; > + } else if (lopts == QDIST_PR_NODECIMAL) { > + llabel = "[1,2)"; > + rlabel = "[2,3]"; > + } else if (lopts == (QDIST_PR_PERCENT | QDIST_PR_NODECIMAL)) { > + llabel = "[1,2)%"; > + rlabel = "[2,3]%"; > + } else if (lopts == QDIST_PR_100X) { > + llabel = "[100.0,166.7)"; > + rlabel = "[233.3,300.0]"; > + } else if (lopts == (QDIST_PR_NOBINRANGE | QDIST_PR_NODECIMAL)) { > + llabel = "1"; > + rlabel = "3"; > + } else { > + g_assert_cmpstr("BUG", ==, "This is not meant to be exhaustive"); > + } > + } > + > + if (llabel) { > + g_string_append(s, llabel); > + } > + if (opt & QDIST_PR_BORDER) { > + g_string_append(s, border); > + } > + > + str = pr_hist(desc, n); > + g_string_append(s, str); > + g_free(str); > + > + if (opt & QDIST_PR_BORDER) { > + g_string_append(s, border); > + } > + if (rlabel) { > + g_string_append(s, rlabel); > + } > + > + str = g_string_free(s, FALSE); > + pr = qdist_pr(&dist, n, opt); > + g_assert_cmpstr(pr, ==, str); > + g_free(pr); > + g_free(str); > + > + qdist_destroy(&dist); > +} > + > +static inline void do_test_pr_label(uint32_t opt) > +{ > + opt |= QDIST_PR_LABELS; > + do_test_pr(opt); > +} > + > +static void test_pr(void) > +{ > + do_test_pr(0); > + > + do_test_pr(QDIST_PR_BORDER); > + > + /* 100X should be ignored because we're not setting LABELS */ > + do_test_pr(QDIST_PR_100X); > + > + do_test_pr_label(0); > + do_test_pr_label(QDIST_PR_NODECIMAL); > + do_test_pr_label(QDIST_PR_PERCENT | QDIST_PR_NODECIMAL); > + do_test_pr_label(QDIST_PR_100X); > + do_test_pr_label(QDIST_PR_NOBINRANGE | QDIST_PR_NODECIMAL); > +} > + > +static void test_bin_shrink(void) > +{ > + static const struct entry_desc a[] = { > + [0] = { 0.0, 42922, 7 }, > + [1] = { 0.25, 47834, 8 }, > + [2] = { 0.50, 26628, 0 }, > + [3] = { 0.625, 597, 4 }, > + [4] = { 0.75, 10298, 1 }, > + [5] = { 0.875, 22, 2 }, > + [6] = { 1.0, 2771, 1 } > + }; > + static const struct entry_desc b[] = { > + [0] = { 0.0, 42922, 7 }, > + [1] = { 0.25, 47834, 8 }, > + [2] = { 0.50, 27225, 3 }, > + [3] = { 0.75, 13091, 1 } > + }; > + > + return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); > +} > + > +static void test_bin_expand(void) > +{ > + static const struct entry_desc a[] = { > + [0] = { 0.0, 11713, 5 }, > + [1] = { 0.25, 20294, 0 }, > + [2] = { 0.50, 17266, 8 }, > + [3] = { 0.625, 1506, 0 }, > + [4] = { 0.75, 10355, 6 }, > + [5] = { 0.833, 2, 1 }, > + [6] = { 0.875, 99, 4 }, > + [7] = { 1.0, 4301, 2 } > + }; > + static const struct entry_desc b[] = { > + [0] = { 0.0, 11713, 5 }, > + [1] = { 0.0, 0, 0 }, > + [2] = { 0.0, 20294, 8 }, > + [3] = { 0.0, 0, 0 }, > + [4] = { 0.0, 0, 0 }, > + [5] = { 0.0, 17266, 6 }, > + [6] = { 0.0, 1506, 1 }, > + [7] = { 0.0, 10355, 4 }, > + [8] = { 0.0, 101, 1 }, > + [9] = { 0.0, 4301, 2 } > + }; > + > + return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); > +} > + > +static void test_bin_simple(void) > +{ > + static const struct entry_desc a[] = { > + [0] = { 10, 101, 8 }, > + [1] = { 11, 0, 0 }, > + [2] = { 12, 2, 1 } > + }; > + static const struct entry_desc b[] = { > + [0] = { 0, 101, 8 }, > + [1] = { 0, 0, 0 }, > + [2] = { 0, 0, 0 }, > + [3] = { 0, 0, 0 }, > + [4] = { 0, 2, 1 } > + }; > + > + return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); > +} > + > +static void test_single_full(void) > +{ > + struct qdist dist; > + > + qdist_init(&dist); > + > + qdist_add(&dist, 3, 102); > + g_assert_cmpfloat(qdist_avg(&dist), ==, 3); > + g_assert_cmpfloat(qdist_xmin(&dist), ==, 3); > + g_assert_cmpfloat(qdist_xmax(&dist), ==, 3); > + > + histogram_check_single_full(&dist, 0); > + histogram_check_single_full(&dist, 1); > + histogram_check_single_full(&dist, 10); > + > + qdist_destroy(&dist); > +} > + > +static void test_single_empty(void) > +{ > + struct qdist dist; > + char *pr; > + > + qdist_init(&dist); > + > + qdist_add(&dist, 3, 0); > + g_assert_cmpuint(qdist_sample_count(&dist), ==, 0); > + g_assert(isnan(qdist_avg(&dist))); > + g_assert_cmpfloat(qdist_xmin(&dist), ==, 3); > + g_assert_cmpfloat(qdist_xmax(&dist), ==, 3); > + > + pr = qdist_pr_plain(&dist, 0); > + g_assert_cmpstr(pr, ==, " "); > + g_free(pr); > + > + pr = qdist_pr_plain(&dist, 1); > + g_assert_cmpstr(pr, ==, " "); > + g_free(pr); > + > + pr = qdist_pr_plain(&dist, 2); > + g_assert_cmpstr(pr, ==, " "); > + g_free(pr); > + > + qdist_destroy(&dist); > +} > + > +static void test_none(void) > +{ > + struct qdist dist; > + char *pr; > + > + qdist_init(&dist); > + > + g_assert(isnan(qdist_avg(&dist))); > + g_assert(isnan(qdist_xmin(&dist))); > + g_assert(isnan(qdist_xmax(&dist))); > + > + pr = qdist_pr_plain(&dist, 0); > + g_assert(pr == NULL); > + > + pr = qdist_pr_plain(&dist, 2); > + g_assert(pr == NULL); > + > + qdist_destroy(&dist); > +} > + > +int main(int argc, char *argv[]) > +{ > + g_test_init(&argc, &argv, NULL); > + g_test_add_func("/qdist/none", test_none); > + g_test_add_func("/qdist/single/empty", test_single_empty); > + g_test_add_func("/qdist/single/full", test_single_full); > + g_test_add_func("/qdist/binning/simple", test_bin_simple); > + g_test_add_func("/qdist/binning/expand", test_bin_expand); > + g_test_add_func("/qdist/binning/shrink", test_bin_shrink); > + g_test_add_func("/qdist/pr", test_pr); > + return g_test_run(); > +}