1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <float.h>
6#include <math.h>
7#include <stdint.h>
8#include <unistd.h>
9
10#include <cobalt-client/cpp/histogram-options.h>
11#include <unittest/unittest.h>
12
13namespace cobalt_client {
14namespace {
15
16bool TestMakeExponentialOptions() {
17    BEGIN_TEST;
18    HistogramOptions options =
19        HistogramOptions::Exponential(/*bucket_count=*/3, /*base=*/4, /*scalar=*/2, /*offset=*/-10);
20    ASSERT_EQ(options.bucket_count, 3);
21    ASSERT_EQ(options.base, 4);
22    ASSERT_EQ(options.scalar, 2);
23    // the calculated offset is such that it guarantees that it matches the lower bound of the first
24    // bucket(excluding underflow bucket). This is to take in account:
25    // offset = scalar * base ^(0) + calculated_offset
26    // calculated_offset = offset - scalar.
27    ASSERT_EQ(options.offset, -12);
28    ASSERT_EQ(options.type, HistogramOptions::Type::kExponential);
29    ASSERT_TRUE(options.IsValid());
30    END_TEST;
31}
32
33bool TestExponentialInvalidBucketCount() {
34    BEGIN_TEST;
35    HistogramOptions options =
36        HistogramOptions::Exponential(/*bucket_count=*/0, /*base=*/1, /*scalar=*/2, /*offset=*/-10);
37    ASSERT_FALSE(options.IsValid());
38    END_TEST;
39}
40
41bool TestExponentialInvalidBase() {
42    BEGIN_TEST;
43    HistogramOptions options =
44        HistogramOptions::Exponential(/*bucket_count=*/1, /*base=*/0, /*scalar=*/2, /*offset=*/-10);
45    ASSERT_FALSE(options.IsValid());
46    END_TEST;
47}
48
49bool TestExponentialInvalidScalar() {
50    BEGIN_TEST;
51    HistogramOptions options =
52        HistogramOptions::Exponential(/*bucket_count=*/1, /*base=*/1, /*scalar=*/0, /*offset=*/-10);
53    ASSERT_FALSE(options.IsValid());
54    END_TEST;
55}
56
57// Verify correct buckect assignment along the boundaries and points within the bucket for
58// exponential bucket width.
59bool TestExponentialMap() {
60    BEGIN_TEST;
61    // This generates the following histogram:
62    //   |      | |  |        |         |
63    // -inf     5 8  14       26      +inf
64    HistogramOptions options =
65        HistogramOptions::Exponential(/*bucket_count=*/3, /*base=*/2, /*scalar=*/3, /*offset=*/5);
66    EXPECT_EQ(options.map_fn(/*value=*/4, options), 0);
67    EXPECT_EQ(options.map_fn(nextafter(5, 4), options), 0);
68    EXPECT_EQ(options.map_fn(5, options), 1);
69    EXPECT_EQ(options.map_fn(7.5, options), 1);
70    EXPECT_EQ(options.map_fn(nextafter(8, 7), options), 1);
71    EXPECT_EQ(options.map_fn(8, options), 2);
72    EXPECT_EQ(options.map_fn(12, options), 2);
73    EXPECT_EQ(options.map_fn(nextafter(12, 11), options), 2);
74    EXPECT_EQ(options.map_fn(14, options), 3);
75    EXPECT_EQ(options.map_fn(18, options), 3);
76    EXPECT_EQ(options.map_fn(nextafter(26, 25), options), 3);
77    EXPECT_EQ(options.map_fn(26, options), 4);
78    END_TEST;
79}
80
81bool TestExponentialReverseMap() {
82    BEGIN_TEST;
83    // This generates the following histogram:
84    //   |      | |  |        |         |
85    // -inf     5 8  14       26      +inf
86    HistogramOptions options =
87        HistogramOptions::Exponential(/*bucket_count=*/3, /*base=*/2, /*scalar=*/3, /*offset=*/5);
88    EXPECT_EQ(options.reverse_map_fn(/*bucket_index=*/0, options), -DBL_MAX);
89    EXPECT_EQ(options.reverse_map_fn(1, options), 5);
90    EXPECT_EQ(options.reverse_map_fn(2, options), 8);
91    EXPECT_EQ(options.reverse_map_fn(3, options), 14);
92    EXPECT_EQ(options.reverse_map_fn(4, options), 26);
93    END_TEST;
94}
95
96bool TestMakeLinearOptions() {
97    BEGIN_TEST;
98    HistogramOptions options =
99        HistogramOptions::Linear(/*bucket_count=*/3, /*scalar=*/2, /*offset=*/-10);
100    ASSERT_EQ(options.bucket_count, 3);
101    ASSERT_EQ(options.base, 1);
102    ASSERT_EQ(options.scalar, 2);
103    ASSERT_EQ(options.offset, -10);
104    ASSERT_EQ(options.type, HistogramOptions::Type::kLinear);
105    ASSERT_TRUE(options.map_fn);
106    ASSERT_TRUE(options.reverse_map_fn);
107    ASSERT_TRUE(options.IsValid());
108    END_TEST;
109}
110
111bool TestLinearInvalidBucketCount() {
112    BEGIN_TEST;
113    HistogramOptions options =
114        HistogramOptions::Linear(/*bucket_count=*/0, /*scalar=*/2, /*offset=*/-10);
115    ASSERT_FALSE(options.IsValid());
116    END_TEST;
117}
118
119bool TestLinearInvalidScalar() {
120    BEGIN_TEST;
121    HistogramOptions options =
122        HistogramOptions::Linear(/*bucket_count=*/1, /*scalar=*/0, /*offset=*/-10);
123    ASSERT_FALSE(options.IsValid());
124    END_TEST;
125}
126
127// Verify correct bucket assignment along the boundaries and points within the bucket for
128// linear bucket width.
129bool TestLinearMap() {
130    BEGIN_TEST;
131    // This generates the following histogram:
132    //   |      |    |   |    |         |
133    // -inf    -10  -8  -6   -4        +inf
134    HistogramOptions options =
135        HistogramOptions::Linear(/*bucket_count=*/3, /*scalar=*/2, /*offset=*/-10);
136    EXPECT_EQ(options.map_fn(/*value=*/-15, options), 0);
137    EXPECT_EQ(options.map_fn(nextafter(-10.0, -11), options), 0);
138    EXPECT_EQ(options.map_fn(-10.0, options), 1);
139    EXPECT_EQ(options.map_fn(-9.0, options), 1);
140    EXPECT_EQ(options.map_fn(-8.0, options), 2);
141    EXPECT_EQ(options.map_fn(-7.0, options), 2);
142    EXPECT_EQ(options.map_fn(-6.0, options), 3);
143    EXPECT_EQ(options.map_fn(-5.0, options), 3);
144    EXPECT_EQ(options.map_fn(nexttoward(-4.0, -5.0), options), 3);
145    EXPECT_EQ(options.map_fn(-4.0, options), 4);
146    END_TEST;
147}
148
149bool TestLinearReverseMap() {
150    BEGIN_TEST;
151    // This generates the following histogram:
152    //   |      |    |   |    |         |
153    // -inf    -10  -8  -6   -4        +inf
154    HistogramOptions options =
155        HistogramOptions::Linear(/*bucket_count=*/3, /*scalar=*/2, /*offset=*/-10);
156    EXPECT_EQ(options.reverse_map_fn(/*bucket_index=*/0, options), -DBL_MAX);
157    EXPECT_EQ(options.reverse_map_fn(1, options), -10);
158    EXPECT_EQ(options.reverse_map_fn(2, options), -8);
159    EXPECT_EQ(options.reverse_map_fn(3, options), -6);
160    EXPECT_EQ(options.reverse_map_fn(4, options), -4);
161    END_TEST;
162}
163
164BEGIN_TEST_CASE(HistogramOptionsTest)
165RUN_TEST(TestMakeExponentialOptions)
166RUN_TEST(TestExponentialInvalidBucketCount)
167RUN_TEST(TestExponentialInvalidBase)
168RUN_TEST(TestExponentialInvalidScalar)
169RUN_TEST(TestExponentialMap)
170RUN_TEST(TestExponentialReverseMap)
171RUN_TEST(TestMakeLinearOptions)
172RUN_TEST(TestLinearInvalidBucketCount)
173RUN_TEST(TestLinearInvalidScalar)
174RUN_TEST(TestLinearMap)
175RUN_TEST(TestLinearReverseMap)
176END_TEST_CASE(HistogramOptionsTest)
177} // namespace
178} // namespace cobalt_client
179