github.com/m3db/m3@v1.5.0/src/x/context/context_test.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package context 22 23 import ( 24 stdctx "context" 25 "fmt" 26 "sync" 27 "sync/atomic" 28 "testing" 29 "time" 30 31 "github.com/opentracing/opentracing-go" 32 "github.com/opentracing/opentracing-go/mocktracer" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 36 xresource "github.com/m3db/m3/src/x/resource" 37 ) 38 39 func TestRegisterFinalizerWithChild(t *testing.T) { 40 xCtx := NewBackground().(*ctx) 41 assert.Nil(t, xCtx.parentCtx()) 42 43 childCtx := xCtx.newChildContext().(*ctx) 44 assert.NotNil(t, childCtx.parentCtx()) 45 46 var ( 47 wg sync.WaitGroup 48 childClosed = false 49 ) 50 51 wg.Add(1) 52 childCtx.RegisterFinalizer(xresource.FinalizerFn(func() { 53 childClosed = true 54 wg.Done() 55 })) 56 57 assert.Equal(t, 0, childCtx.numFinalizeables()) 58 59 childCtx.Close() 60 wg.Wait() 61 62 // validate closing childCtx, closes the parent 63 assert.True(t, xCtx.IsClosed()) 64 assert.True(t, childClosed) 65 } 66 67 func TestRegisterFinalizer(t *testing.T) { 68 var ( 69 wg sync.WaitGroup 70 closed = false 71 ctx = NewBackground().(*ctx) 72 ) 73 74 wg.Add(1) 75 ctx.RegisterFinalizer(xresource.FinalizerFn(func() { 76 closed = true 77 wg.Done() 78 })) 79 80 assert.Equal(t, 1, ctx.numFinalizeables()) 81 82 ctx.Close() 83 wg.Wait() 84 85 assert.Equal(t, true, closed) 86 } 87 88 func TestRegisterCloserWithChild(t *testing.T) { 89 xCtx := NewBackground().(*ctx) 90 assert.Nil(t, xCtx.parentCtx()) 91 92 childCtx := xCtx.newChildContext().(*ctx) 93 assert.NotNil(t, childCtx.parentCtx()) 94 95 var ( 96 wg sync.WaitGroup 97 childClosed = false 98 ) 99 100 wg.Add(1) 101 childCtx.RegisterCloser(xresource.SimpleCloserFn(func() { 102 childClosed = true 103 wg.Done() 104 })) 105 106 assert.Equal(t, 0, childCtx.numFinalizeables()) 107 108 childCtx.Close() 109 wg.Wait() 110 111 // validate closing childCtx, closes the parent 112 assert.True(t, xCtx.IsClosed()) 113 assert.True(t, childClosed) 114 } 115 116 func TestRegisterCloser(t *testing.T) { 117 var ( 118 wg sync.WaitGroup 119 closed = false 120 ctx = NewBackground().(*ctx) 121 ) 122 123 wg.Add(1) 124 ctx.RegisterCloser(xresource.SimpleCloserFn(func() { 125 closed = true 126 wg.Done() 127 })) 128 129 assert.Equal(t, 1, ctx.numFinalizeables()) 130 131 ctx.Close() 132 wg.Wait() 133 134 assert.Equal(t, true, closed) 135 } 136 137 func TestDoesNotRegisterFinalizerWhenClosed(t *testing.T) { 138 ctx := NewBackground().(*ctx) 139 ctx.Close() 140 ctx.RegisterFinalizer(xresource.FinalizerFn(func() {})) 141 142 assert.Equal(t, 0, ctx.numFinalizeables()) 143 } 144 145 func TestDoesNotCloseTwice(t *testing.T) { 146 ctx := NewBackground().(*ctx) 147 148 var closed int32 149 ctx.RegisterFinalizer(xresource.FinalizerFn(func() { 150 atomic.AddInt32(&closed, 1) 151 })) 152 153 ctx.Close() 154 ctx.Close() 155 156 // Give some time for a bug to occur. 157 time.Sleep(100 * time.Millisecond) 158 159 assert.Equal(t, int32(1), atomic.LoadInt32(&closed)) 160 } 161 162 func TestDependsOnNoCloserAllocation(t *testing.T) { 163 ctx := NewBackground().(*ctx) 164 ctx.DependsOn(NewBackground()) 165 assert.Equal(t, 0, ctx.numFinalizeables()) 166 } 167 168 func TestDependsOn(t *testing.T) { 169 ctx := NewBackground().(*ctx) 170 testDependsOn(t, ctx) 171 } 172 173 func TestDependsOnWithReset(t *testing.T) { 174 ctx := NewBackground().(*ctx) 175 176 testDependsOn(t, ctx) 177 178 // Reset and test works again. 179 ctx.Reset() 180 181 testDependsOn(t, ctx) 182 } 183 184 func testDependsOn(t *testing.T, c *ctx) { 185 var wg sync.WaitGroup 186 var closed int32 187 188 other := NewBackground().(*ctx) 189 190 wg.Add(1) 191 c.RegisterFinalizer(xresource.FinalizerFn(func() { 192 atomic.AddInt32(&closed, 1) 193 wg.Done() 194 })) 195 196 c.DependsOn(other) 197 c.Close() 198 199 // Give some time for a bug to occur. 200 time.Sleep(100 * time.Millisecond) 201 202 // Ensure still not closed 203 assert.Equal(t, int32(0), atomic.LoadInt32(&closed)) 204 205 // Now close the context ctx is dependent on. 206 other.BlockingClose() 207 208 wg.Wait() 209 210 // Ensure closed now. 211 assert.Equal(t, int32(1), atomic.LoadInt32(&closed)) 212 } 213 214 func TestDependsOnWithChild(t *testing.T) { 215 var ( 216 c = NewBackground().(*ctx) 217 child = c.newChildContext().(*ctx) 218 other = NewBackground().(*ctx) 219 220 wg sync.WaitGroup 221 closed int32 222 ) 223 224 wg.Add(1) 225 c.RegisterFinalizer(xresource.FinalizerFn(func() { 226 atomic.AddInt32(&closed, 1) 227 wg.Done() 228 })) 229 230 child.DependsOn(other) 231 child.Close() 232 233 // Give some time for a bug to occur. 234 time.Sleep(100 * time.Millisecond) 235 236 // Ensure still not closed even though child ctx has been closed 237 assert.Equal(t, int32(0), atomic.LoadInt32(&closed)) 238 239 // Now close the context ctx is dependent on. 240 other.BlockingClose() 241 242 wg.Wait() 243 244 // Ensure closed now. 245 assert.Equal(t, int32(1), atomic.LoadInt32(&closed)) 246 assert.True(t, c.IsClosed()) 247 } 248 249 func TestSampledTraceSpan(t *testing.T) { 250 var ( 251 xCtx = NewBackground() 252 goCtx = stdctx.Background() 253 sp opentracing.Span 254 spCtx Context 255 ) 256 257 // use a mock tracer to ensure sampling rate is set to 1. 258 tracer := mocktracer.New() 259 sp = tracer.StartSpan("foo") 260 defer sp.Finish() 261 262 mockSpan, ok := sp.(*mocktracer.MockSpan) 263 require.True(t, ok) 264 265 mockSpan.SpanContext.Sampled = true 266 267 spGoCtx := opentracing.ContextWithSpan(goCtx, sp) 268 269 xCtx.SetGoContext(spGoCtx) 270 spCtx, sp = xCtx.StartTraceSpan("test_op_2") 271 assert.NotNil(t, sp) 272 defer sp.Finish() 273 274 childCtx := spCtx.(*ctx) 275 assert.NotNil(t, childCtx.parentCtx()) 276 } 277 278 func TestGoContext(t *testing.T) { 279 goCtx, cancel := stdctx.WithTimeout(stdctx.Background(), time.Minute) 280 defer cancel() 281 xCtx := NewWithGoContext(goCtx) 282 283 var ( 284 returnCtx stdctx.Context 285 ) 286 287 returnCtx = xCtx.GoContext() 288 assert.Equal(t, goCtx, returnCtx) 289 290 xCtx.Reset() 291 returnCtx = xCtx.GoContext() 292 assert.Equal(t, stdctx.Background(), returnCtx) 293 } 294 295 // Test that too deep span tree created is prevented 296 // Span is marked as error in that case that which is well defined 297 func TestTooDeepSpanTreeIsPreventedAndMarked(t *testing.T) { 298 // use a mock tracer to ensure sampling rate is set to 1. 299 tracer := mocktracer.New() 300 301 span := tracer.StartSpan("root-span") 302 defer span.Finish() 303 304 context := NewWithGoContext(opentracing.ContextWithSpan(stdctx.Background(), span)) 305 306 var ( 307 lastChildSpanCreated opentracing.Span 308 lastChildContextCreated = context 309 ) 310 for i := 1; i <= maxDistanceFromRootContext; i++ { 311 lastChildContextCreated, lastChildSpanCreated, _ = 312 lastChildContextCreated.StartSampledTraceSpan(fmt.Sprintf("test-action-depth-%d", i)) 313 } 314 315 mockSpan := lastChildSpanCreated.(*mocktracer.MockSpan) 316 317 errorTagValue := mockSpan.Tag("error") 318 assert.NotNil(t, errorTagValue) 319 assert.Equal(t, true, errorTagValue) 320 spanLogs := mockSpan.Logs() 321 assert.Len(t, spanLogs, 1) 322 spanLog := spanLogs[0] 323 assert.True(t, fieldsContains(spanLog.Fields, "event", "error") && 324 fieldsContains(spanLog.Fields, "error.object", errSpanTooDeep.Error())) 325 326 childContext, _, _ := lastChildContextCreated.StartSampledTraceSpan("another-span-beyond-max-depth") 327 assert.Equal(t, lastChildContextCreated, childContext) 328 } 329 330 func fieldsContains(fields []mocktracer.MockKeyValue, key string, value string) bool { 331 for _, field := range fields { 332 if field.Key == key && field.ValueString == value { 333 return true 334 } 335 } 336 return false 337 }