github.com/GoogleCloudPlatform/testgrid@v0.0.174/util/metrics/metrics_test.go (about) 1 /* 2 Copyright 2021 The TestGrid Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package metrics 18 19 import ( 20 "fmt" 21 "sync" 22 "testing" 23 "time" 24 ) 25 26 func TestCyclic(t *testing.T) { 27 testcases := []struct { 28 name string 29 method func(Cyclic) 30 expectSeconds time.Duration 31 expectFails int64 32 expectSuccess int64 33 expectSkips int64 34 }{ 35 { 36 name: "success", 37 method: func(c Cyclic) { 38 fin := c.Start() 39 time.Sleep(1 * time.Second) 40 fin.Success() 41 }, 42 expectSeconds: 1 * time.Second, 43 expectSuccess: 1, 44 }, 45 { 46 name: "error", 47 method: func(c Cyclic) { 48 fin := c.Start() 49 time.Sleep(2 * time.Second) 50 fin.Fail() 51 }, 52 expectSeconds: 2 * time.Second, 53 expectFails: 1, 54 }, 55 { 56 name: "skips", 57 method: func(c Cyclic) { 58 fin := c.Start() 59 time.Sleep(200 * time.Millisecond) 60 fin.Skip() 61 }, 62 expectSeconds: 200 * time.Millisecond, 63 expectSkips: 1, 64 }, 65 { 66 name: "counting", 67 method: func(c Cyclic) { 68 for i := 0; i < 5; i++ { 69 ok := c.Start() 70 ok.Success() 71 no := c.Start() 72 no.Fail() 73 } 74 }, 75 expectSuccess: 5, 76 expectFails: 5, 77 }, 78 } 79 80 for _, test := range testcases { 81 t.Run(test.name, func(t *testing.T) { 82 var errorCounter, skipCounter, successCounter FakeCounter 83 cycleDuration := FakeDuration{read: true} 84 85 cyclic := Cyclic{ 86 errors: &errorCounter, 87 skips: &skipCounter, 88 successes: &successCounter, 89 cycleSeconds: &cycleDuration, 90 } 91 92 fudge := 200 * time.Millisecond 93 94 test.method(cyclic) 95 96 if test.expectFails != errorCounter.count { 97 t.Errorf("Want %d errors, got %d", test.expectFails, errorCounter.count) 98 } 99 if test.expectSkips != skipCounter.count { 100 t.Errorf("Want %d skips, got %d", test.expectSkips, skipCounter.count) 101 } 102 if test.expectSuccess != successCounter.count { 103 t.Errorf("Want %d successes, got %d", test.expectSuccess, successCounter.count) 104 } 105 106 if cycleDuration.last < test.expectSeconds || (test.expectSeconds+fudge) < cycleDuration.last { 107 t.Errorf("Expected between %d and %d seconds, got %d", test.expectSeconds, test.expectSeconds+fudge, cycleDuration.last) 108 } 109 }) 110 } 111 112 } 113 114 func TestCyclic_TolerateNilCounters(t *testing.T) { 115 for a, errorCounter := range []Counter{nil, &FakeCounter{}} { 116 for b, skipCounter := range []Counter{nil, &FakeCounter{}} { 117 for c, successCounter := range []Counter{nil, &FakeCounter{}} { 118 for d, cycleDuration := range []Duration{nil, &FakeDuration{}} { 119 t.Run(fmt.Sprintf("Error-%d Skip-%d Success-%d Cycle-%d", a, b, c, d), func(t *testing.T) { 120 cyclic := Cyclic{ 121 errors: errorCounter, 122 skips: skipCounter, 123 successes: successCounter, 124 cycleSeconds: cycleDuration, 125 } 126 var wg sync.WaitGroup 127 wg.Add(3) 128 go func() { 129 f := cyclic.Start() 130 f.Success() 131 wg.Done() 132 }() 133 go func() { 134 f := cyclic.Start() 135 f.Skip() 136 wg.Done() 137 }() 138 go func() { 139 f := cyclic.Start() 140 f.Fail() 141 wg.Done() 142 }() 143 wg.Wait() 144 }) 145 } 146 } 147 } 148 } 149 } 150 151 type FakeCounter struct { 152 count int64 153 } 154 155 func (f *FakeCounter) Name() string { 156 return "FakeCounter" 157 } 158 159 func (f *FakeCounter) Add(n int64, _ ...string) { 160 f.count += n 161 } 162 163 type FakeInt64 struct { 164 read bool // Fake implementation not concurrent-safe 165 last int64 166 } 167 168 func (f *FakeInt64) Name() string { 169 return "FakeInt64" 170 } 171 172 func (f *FakeInt64) Set(n int64, _ ...string) { 173 if f.read { 174 f.last = n 175 } 176 } 177 178 type FakeDuration struct { 179 read bool // Fake implementation not concurrent-safe 180 last time.Duration 181 } 182 183 func (f *FakeDuration) Name() string { 184 return "FakeDuration" 185 } 186 187 func (f *FakeDuration) Set(n time.Duration, _ ...string) { 188 if f.read { 189 f.last = n 190 } 191 }