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  }