1// Copyright The OpenTelemetry Authors 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 15package otel 16 17import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "log" 22 "testing" 23 "time" 24 25 "github.com/stretchr/testify/suite" 26) 27 28type errLogger []string 29 30func (l *errLogger) Write(p []byte) (int, error) { 31 msg := bytes.TrimRight(p, "\n") 32 (*l) = append(*l, string(msg)) 33 return len(msg), nil 34} 35 36func (l *errLogger) Reset() { 37 *l = errLogger([]string{}) 38} 39 40func (l *errLogger) Got() []string { 41 return []string(*l) 42} 43 44type HandlerTestSuite struct { 45 suite.Suite 46 47 origHandler *loggingErrorHandler 48 errLogger *errLogger 49} 50 51func (s *HandlerTestSuite) SetupSuite() { 52 s.errLogger = new(errLogger) 53 s.origHandler = globalErrorHandler 54 globalErrorHandler = &loggingErrorHandler{ 55 l: log.New(s.errLogger, "", 0), 56 } 57} 58 59func (s *HandlerTestSuite) TearDownSuite() { 60 globalErrorHandler = s.origHandler 61} 62 63func (s *HandlerTestSuite) SetupTest() { 64 s.errLogger.Reset() 65} 66 67func (s *HandlerTestSuite) TestGlobalHandler() { 68 errs := []string{"one", "two"} 69 GetErrorHandler().Handle(errors.New(errs[0])) 70 Handle(errors.New(errs[1])) 71 s.Assert().Equal(errs, s.errLogger.Got()) 72} 73 74func (s *HandlerTestSuite) TestNoDropsOnDelegate() { 75 // max time to wait for goroutine to Handle an error. 76 pause := 10 * time.Millisecond 77 78 var sent int 79 err := errors.New("") 80 stop := make(chan struct{}) 81 beat := make(chan struct{}) 82 done := make(chan struct{}) 83 84 // Wait for a error to be submitted from the following goroutine. 85 wait := func(d time.Duration) error { 86 timer := time.NewTimer(d) 87 select { 88 case <-timer.C: 89 // We are about to fail, stop the spawned goroutine. 90 stop <- struct{}{} 91 return fmt.Errorf("no errors sent in %v", d) 92 case <-beat: 93 // Allow the timer to be reclaimed by GC. 94 timer.Stop() 95 return nil 96 } 97 } 98 99 go func() { 100 // Slow down to speed up: do not overload the processor. 101 ticker := time.NewTicker(100 * time.Microsecond) 102 for { 103 select { 104 case <-stop: 105 ticker.Stop() 106 done <- struct{}{} 107 return 108 case <-ticker.C: 109 sent++ 110 Handle(err) 111 } 112 113 select { 114 case beat <- struct{}{}: 115 default: 116 } 117 } 118 }() 119 120 // Wait for the spice to flow 121 s.Require().NoError(wait(pause), "starting error stream") 122 123 // Change to another Handler. We are testing this is loss-less. 124 newErrLogger := new(errLogger) 125 secondary := &loggingErrorHandler{ 126 l: log.New(newErrLogger, "", 0), 127 } 128 SetErrorHandler(secondary) 129 s.Require().NoError(wait(pause), "switched to new Handler") 130 131 // Testing done, stop sending errors. 132 stop <- struct{}{} 133 // Ensure we do not lose any straglers. 134 <-done 135 136 got := append(s.errLogger.Got(), newErrLogger.Got()...) 137 s.Assert().Greater(len(got), 1, "at least 2 errors should have been sent") 138 s.Assert().Len(got, sent) 139} 140 141func TestHandlerTestSuite(t *testing.T) { 142 suite.Run(t, new(HandlerTestSuite)) 143} 144