OLD | NEW |
(Empty) | |
| 1 # Copyright 2016 Google Inc. All Rights Reserved. |
| 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at |
| 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. |
| 14 |
| 15 from __future__ import absolute_import |
| 16 |
| 17 import sys |
| 18 import unittest2 |
| 19 from expects import expect, equal, raise_error |
| 20 |
| 21 from google.api.control import distribution, messages |
| 22 |
| 23 |
| 24 class TestCreateExponential(unittest2.TestCase): |
| 25 |
| 26 def test_should_fail_if_num_finite_buckets_is_bad(self): |
| 27 testf = lambda: distribution.create_exponential(0, 1.1, 0.1) |
| 28 expect(testf).to(raise_error(ValueError)) |
| 29 |
| 30 def test_should_fail_if_growth_factor_is_bad(self): |
| 31 testf = lambda: distribution.create_exponential(1, 0.9, 0.1) |
| 32 expect(testf).to(raise_error(ValueError)) |
| 33 |
| 34 def test_should_fail_if_scale_is_bad(self): |
| 35 testf = lambda: distribution.create_exponential(1, 1.1, -0.1) |
| 36 expect(testf).to(raise_error(ValueError)) |
| 37 |
| 38 def test_should_succeed_if_inputs_are_ok(self): |
| 39 num_finite_buckets = 1 |
| 40 got = distribution.create_exponential(num_finite_buckets, 1.1, 0.1) |
| 41 expect(len(got.bucketCounts)).to(equal(num_finite_buckets + 2)) |
| 42 |
| 43 |
| 44 class TestCreateLinear(unittest2.TestCase): |
| 45 |
| 46 def test_should_fail_if_num_finite_buckets_is_bad(self): |
| 47 testf = lambda: distribution.create_linear(0, 1.1, 0.1) |
| 48 expect(testf).to(raise_error(ValueError)) |
| 49 |
| 50 def test_should_fail_if_growth_factor_is_bad(self): |
| 51 testf = lambda: distribution.create_linear(1, -0.1, 0.1) |
| 52 expect(testf).to(raise_error(ValueError)) |
| 53 |
| 54 def test_should_succeed_if_inputs_are_ok(self): |
| 55 num_finite_buckets = 1 |
| 56 got = distribution.create_linear(num_finite_buckets, 0.1, 0.1) |
| 57 expect(len(got.bucketCounts)).to(equal(num_finite_buckets + 2)) |
| 58 |
| 59 |
| 60 class TestCreateExplicit(unittest2.TestCase): |
| 61 |
| 62 def test_should_fail_if_there_are_matching_bounds(self): |
| 63 testf = lambda: distribution.create_explicit([0.0, 0.1, 0.1]) |
| 64 expect(testf).to(raise_error(ValueError)) |
| 65 |
| 66 def test_should_succeed_if_inputs_are_ok(self): |
| 67 want = [0.1, 0.2, 0.3] |
| 68 got = distribution.create_explicit([0.1, 0.2, 0.3]) |
| 69 expect(got.explicitBuckets.bounds).to(equal(want)) |
| 70 expect(len(got.bucketCounts)).to(equal(len(want) + 1)) |
| 71 |
| 72 def test_should_succeed_if_input_bounds_are_unsorted(self): |
| 73 want = [0.1, 0.2, 0.3] |
| 74 got = distribution.create_explicit([0.3, 0.1, 0.2]) |
| 75 expect(got.explicitBuckets.bounds).to(equal(want)) |
| 76 |
| 77 |
| 78 def _make_explicit_dist(): |
| 79 return distribution.create_explicit([0.1, 0.3, 0.5, 0.7]) |
| 80 |
| 81 |
| 82 def _make_linear_dist(): |
| 83 return distribution.create_linear(3, 0.2, 0.1) |
| 84 |
| 85 |
| 86 def _make_exponential_dist(): |
| 87 return distribution.create_exponential(3, 2, 0.1) |
| 88 |
| 89 _UNDERFLOW_SAMPLE = 1e-5 |
| 90 _LOW_SAMPLE = 0.11 |
| 91 _HIGH_SAMPLE = 0.5 |
| 92 _OVERFLOW_SAMPLE = 1e5 |
| 93 |
| 94 _TEST_SAMPLES_AND_BUCKETS = [ |
| 95 { |
| 96 'samples': [_UNDERFLOW_SAMPLE], |
| 97 'want': [1, 0, 0, 0, 0] |
| 98 }, |
| 99 { |
| 100 'samples': [_LOW_SAMPLE] * 2, |
| 101 'want': [0, 2, 0, 0, 0] |
| 102 }, |
| 103 { |
| 104 'samples': [_LOW_SAMPLE, _HIGH_SAMPLE, _HIGH_SAMPLE], |
| 105 'want': [0, 1, 0, 2, 0] |
| 106 }, |
| 107 { |
| 108 'samples': [_OVERFLOW_SAMPLE], |
| 109 'want': [0, 0, 0, 0, 1] |
| 110 }, |
| 111 ] |
| 112 |
| 113 |
| 114 def _expect_stats_eq_direct_calc_from_samples(d, samples): |
| 115 # pylint: disable=fixme |
| 116 # TODO: update this the sum of rho-squared |
| 117 want_mean = sum(samples) / len(samples) |
| 118 expect(d.mean).to(equal(want_mean)) |
| 119 expect(d.maximum).to(equal(max(samples))) |
| 120 expect(d.minimum).to(equal(min(samples))) |
| 121 |
| 122 |
| 123 class TestAddSample(unittest2.TestCase): |
| 124 NOTHING_SET = messages.Distribution() |
| 125 |
| 126 def test_should_fail_if_no_buckets_are_set(self): |
| 127 testf = lambda: distribution.add_sample(_UNDERFLOW_SAMPLE, |
| 128 self.NOTHING_SET) |
| 129 expect(testf).to(raise_error(ValueError)) |
| 130 |
| 131 def expect_adds_test_samples_ok(self, make_dist_func): |
| 132 for t in _TEST_SAMPLES_AND_BUCKETS: |
| 133 d = make_dist_func() |
| 134 samples = t['samples'] |
| 135 for s in samples: |
| 136 distribution.add_sample(s, d) |
| 137 expect(d.bucketCounts).to(equal(t['want'])) |
| 138 _expect_stats_eq_direct_calc_from_samples(d, samples) |
| 139 |
| 140 def test_update_explict_buckets_ok(self): |
| 141 self.expect_adds_test_samples_ok(_make_explicit_dist) |
| 142 |
| 143 def test_update_exponential_buckets_ok(self): |
| 144 self.expect_adds_test_samples_ok(_make_exponential_dist) |
| 145 |
| 146 def test_update_linear_buckets_ok(self): |
| 147 self.expect_adds_test_samples_ok(_make_linear_dist) |
| 148 |
| 149 |
| 150 class TestMerge(unittest2.TestCase): |
| 151 |
| 152 def setUp(self): |
| 153 self.merge_triples = ( |
| 154 ( |
| 155 distribution.create_exponential(3, 2, 0.1), |
| 156 distribution.create_exponential(3, 2, 0.1), |
| 157 distribution.create_exponential(4, 2, 0.1), |
| 158 ),( |
| 159 distribution.create_linear(3, 0.2, 0.1), |
| 160 distribution.create_linear(3, 0.2, 0.1), |
| 161 distribution.create_linear(4, 0.2, 0.1) |
| 162 ),( |
| 163 distribution.create_explicit([0.1, 0.3]), |
| 164 distribution.create_explicit([0.1, 0.3]), |
| 165 distribution.create_explicit([0.1, 0.3, 0.5]), |
| 166 ) |
| 167 ) |
| 168 for d1, d2, _ in self.merge_triples: |
| 169 distribution.add_sample(_LOW_SAMPLE, d1) |
| 170 distribution.add_sample(_HIGH_SAMPLE, d2) |
| 171 |
| 172 def test_should_fail_on_dissimilar_bucket_options(self): |
| 173 explicit = _make_explicit_dist() |
| 174 linear = _make_linear_dist() |
| 175 exponential = _make_exponential_dist() |
| 176 pairs = ( |
| 177 (explicit, linear), |
| 178 (explicit, exponential), |
| 179 (linear, exponential) |
| 180 ) |
| 181 for p in pairs: |
| 182 testf = lambda: distribution.merge(*p) |
| 183 expect(testf).to(raise_error(ValueError)) |
| 184 |
| 185 def test_should_fail_on_dissimilar_bucket_counts(self): |
| 186 for _, d2, d3 in self.merge_triples: |
| 187 testf = lambda: distribution.merge(d2, d3) |
| 188 expect(testf).to(raise_error(ValueError)) |
| 189 |
| 190 def test_should_merge_stats_correctly(self): |
| 191 # TODO(add a check of the variance) |
| 192 for d1, d2, _ in self.merge_triples: |
| 193 distribution.merge(d1, d2) |
| 194 expect(d2.count).to(equal(2)) |
| 195 expect(d2.mean).to(equal((_HIGH_SAMPLE + _LOW_SAMPLE) / 2)) |
| 196 expect(d2.maximum).to(equal(_HIGH_SAMPLE)) |
| 197 expect(d2.minimum).to(equal(_LOW_SAMPLE)) |
| 198 |
| 199 def test_should_merge_bucket_counts_correctly(self): |
| 200 for d1, d2, _ in self.merge_triples: |
| 201 d1_start = list(d1.bucketCounts) |
| 202 d2_start = list(d2.bucketCounts) |
| 203 want = [x + y for (x,y) in zip(d1_start, d2_start)] |
| 204 distribution.merge(d1, d2) |
| 205 expect(d2.bucketCounts).to(equal(want)) |
OLD | NEW |