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  }