github.com/xmidt-org/webpa-common@v1.11.9/semaphore/instrument_test.go (about)

     1  package semaphore
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/go-kit/kit/metrics/generic"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestWithResources(t *testing.T) {
    14  	var (
    15  		assert = assert.New(t)
    16  		io     = new(instrumentOptions)
    17  
    18  		custom = generic.NewCounter("test")
    19  	)
    20  
    21  	WithResources(nil)(io)
    22  	assert.NotNil(io.resources)
    23  
    24  	WithResources(custom)(io)
    25  	assert.Equal(custom, io.resources)
    26  }
    27  
    28  func TestWithFailures(t *testing.T) {
    29  	var (
    30  		assert = assert.New(t)
    31  		io     = new(instrumentOptions)
    32  
    33  		custom = generic.NewCounter("test")
    34  	)
    35  
    36  	WithFailures(nil)(io)
    37  	assert.NotNil(io.failures)
    38  
    39  	WithFailures(custom)(io)
    40  	assert.Equal(custom, io.failures)
    41  }
    42  
    43  func TestWithClosed(t *testing.T) {
    44  	var (
    45  		assert = assert.New(t)
    46  		io     = new(instrumentOptions)
    47  
    48  		custom = generic.NewGauge("test")
    49  	)
    50  
    51  	WithClosed(nil)(io)
    52  	assert.NotNil(io.closed)
    53  
    54  	WithClosed(custom)(io)
    55  	assert.Equal(custom, io.closed)
    56  }
    57  
    58  func testInstrumentNilSemaphore(t *testing.T) {
    59  	assert.Panics(t,
    60  		func() {
    61  			Instrument(nil)
    62  		},
    63  	)
    64  }
    65  
    66  func TestInstrument(t *testing.T) {
    67  	t.Run("NilSemaphore", testInstrumentNilSemaphore)
    68  }
    69  
    70  func testInstrumentCloseableNilSemaphore(t *testing.T) {
    71  	assert.Panics(t,
    72  		func() {
    73  			InstrumentCloseable(nil)
    74  		},
    75  	)
    76  }
    77  
    78  func TestInstrumentCloseable(t *testing.T) {
    79  	t.Run("NilSemaphore", testInstrumentCloseableNilSemaphore)
    80  }
    81  
    82  func testInstrumentedSemaphoreAcquireSuccess(t *testing.T) {
    83  	var (
    84  		assert    = assert.New(t)
    85  		resources = generic.NewCounter("test")
    86  		failures  = generic.NewCounter("test")
    87  		s         = Instrument(Mutex(), WithResources(resources), WithFailures(failures))
    88  
    89  		result = make(chan error)
    90  	)
    91  
    92  	go func() {
    93  		result <- s.Acquire()
    94  	}()
    95  
    96  	select {
    97  	case err := <-result:
    98  		assert.NoError(err)
    99  		assert.Equal(float64(1.0), resources.Value())
   100  		assert.Zero(failures.Value())
   101  
   102  		assert.NoError(s.Release())
   103  		assert.Zero(resources.Value())
   104  		assert.Zero(failures.Value())
   105  	case <-time.After(time.Second):
   106  		assert.FailNow("Acquire blocked unexpectedly")
   107  	}
   108  }
   109  
   110  func testInstrumentedSemaphoreAcquireFail(t *testing.T) {
   111  	var (
   112  		assert    = assert.New(t)
   113  		resources = generic.NewCounter("test")
   114  		failures  = generic.NewCounter("test")
   115  		cm        = CloseableMutex()
   116  		s         = Instrument(cm, WithResources(resources), WithFailures(failures))
   117  
   118  		result = make(chan error)
   119  	)
   120  
   121  	go func() {
   122  		cm.Close()
   123  		result <- s.Acquire()
   124  	}()
   125  
   126  	select {
   127  	case err := <-result:
   128  		assert.Equal(ErrClosed, err)
   129  		assert.Zero(resources.Value())
   130  		assert.Equal(float64(1.0), failures.Value())
   131  
   132  		assert.Equal(ErrClosed, s.Release()) // idempotent
   133  		assert.Zero(resources.Value())
   134  		assert.Equal(float64(1.0), failures.Value())
   135  	case <-time.After(time.Second):
   136  		assert.FailNow("Acquire blocked unexpectedly")
   137  	}
   138  }
   139  
   140  func testInstrumentedSemaphoreTryAcquire(t *testing.T) {
   141  	var (
   142  		assert    = assert.New(t)
   143  		require   = require.New(t)
   144  		resources = generic.NewCounter("test")
   145  		failures  = generic.NewCounter("test")
   146  		s         = Instrument(Mutex(), WithResources(resources), WithFailures(failures))
   147  	)
   148  
   149  	assert.Zero(resources.Value())
   150  	assert.Zero(failures.Value())
   151  
   152  	require.True(s.TryAcquire())
   153  	assert.Equal(float64(1.0), resources.Value())
   154  	assert.Zero(failures.Value())
   155  
   156  	require.False(s.TryAcquire())
   157  	assert.Equal(float64(1.0), resources.Value())
   158  	assert.Equal(float64(1.0), failures.Value())
   159  
   160  	assert.NoError(s.Release())
   161  	assert.Zero(resources.Value())
   162  	assert.Equal(float64(1.0), failures.Value())
   163  }
   164  
   165  func testInstrumentedSemaphoreAcquireWaitSuccess(t *testing.T) {
   166  	var (
   167  		assert    = assert.New(t)
   168  		resources = generic.NewCounter("test")
   169  		failures  = generic.NewCounter("test")
   170  		s         = Instrument(Mutex(), WithResources(resources), WithFailures(failures))
   171  
   172  		ready  = make(chan struct{})
   173  		result = make(chan error)
   174  		timer  = make(chan time.Time)
   175  	)
   176  
   177  	go func() {
   178  		s.Acquire()
   179  		close(ready)
   180  		result <- s.AcquireWait(timer)
   181  	}()
   182  
   183  	select {
   184  	case <-ready:
   185  		assert.Equal(float64(1.0), resources.Value())
   186  		assert.Zero(failures.Value())
   187  		s.Release()
   188  	case <-time.After(time.Second):
   189  		assert.FailNow("Failed to spawn AcquireWait goroutine")
   190  	}
   191  
   192  	select {
   193  	case err := <-result:
   194  		assert.NoError(err)
   195  		assert.Equal(float64(1.0), resources.Value())
   196  		assert.Zero(failures.Value())
   197  
   198  		assert.NoError(s.Release())
   199  		assert.Zero(resources.Value())
   200  		assert.Zero(failures.Value())
   201  	case <-time.After(time.Second):
   202  		assert.FailNow("AcquireWait blocked unexpectedly")
   203  	}
   204  }
   205  
   206  func testInstrumentedSemaphoreAcquireWaitTimeout(t *testing.T) {
   207  	var (
   208  		assert    = assert.New(t)
   209  		resources = generic.NewCounter("test")
   210  		failures  = generic.NewCounter("test")
   211  		s         = Instrument(Mutex(), WithResources(resources), WithFailures(failures))
   212  
   213  		ready  = make(chan struct{})
   214  		result = make(chan error)
   215  		timer  = make(chan time.Time)
   216  	)
   217  
   218  	go func() {
   219  		s.Acquire()
   220  		close(ready)
   221  		result <- s.AcquireWait(timer)
   222  	}()
   223  
   224  	select {
   225  	case <-ready:
   226  		assert.Equal(float64(1.0), resources.Value())
   227  		assert.Zero(failures.Value())
   228  		timer <- time.Time{}
   229  	case <-time.After(time.Second):
   230  		assert.FailNow("Failed to spawn AcquireWait goroutine")
   231  	}
   232  
   233  	select {
   234  	case err := <-result:
   235  		assert.Equal(ErrTimeout, err)
   236  		assert.Equal(float64(1.0), resources.Value())
   237  		assert.Equal(float64(1.0), failures.Value())
   238  
   239  		s.Release()
   240  		assert.Zero(resources.Value())
   241  		assert.Equal(float64(1.0), failures.Value())
   242  	case <-time.After(time.Second):
   243  		assert.FailNow("AcquireWait blocked unexpectedly")
   244  	}
   245  }
   246  
   247  func testInstrumentedSemaphoreAcquireCtxSuccess(t *testing.T) {
   248  	var (
   249  		assert    = assert.New(t)
   250  		resources = generic.NewCounter("test")
   251  		failures  = generic.NewCounter("test")
   252  		s         = Instrument(Mutex(), WithResources(resources), WithFailures(failures))
   253  
   254  		ready       = make(chan struct{})
   255  		result      = make(chan error)
   256  		ctx, cancel = context.WithCancel(context.Background())
   257  	)
   258  
   259  	defer cancel()
   260  
   261  	go func() {
   262  		s.Acquire()
   263  		close(ready)
   264  		result <- s.AcquireCtx(ctx)
   265  	}()
   266  
   267  	select {
   268  	case <-ready:
   269  		assert.Equal(float64(1.0), resources.Value())
   270  		assert.Zero(failures.Value())
   271  		s.Release()
   272  	case <-time.After(time.Second):
   273  		assert.FailNow("Failed to spawn AcquireCtx goroutine")
   274  	}
   275  
   276  	select {
   277  	case err := <-result:
   278  		assert.NoError(err)
   279  		assert.Equal(float64(1.0), resources.Value())
   280  		assert.Zero(failures.Value())
   281  
   282  		s.Release()
   283  		assert.Zero(resources.Value())
   284  		assert.Zero(failures.Value())
   285  	case <-time.After(time.Second):
   286  		assert.FailNow("AcquireCtx blocked unexpectedly")
   287  	}
   288  }
   289  
   290  func testInstrumentedSemaphoreAcquireCtxCancel(t *testing.T) {
   291  	var (
   292  		assert    = assert.New(t)
   293  		resources = generic.NewCounter("test")
   294  		failures  = generic.NewCounter("test")
   295  		s         = Instrument(Mutex(), WithResources(resources), WithFailures(failures))
   296  
   297  		ready       = make(chan struct{})
   298  		result      = make(chan error)
   299  		ctx, cancel = context.WithCancel(context.Background())
   300  	)
   301  
   302  	defer cancel()
   303  
   304  	go func() {
   305  		s.Acquire()
   306  		close(ready)
   307  		result <- s.AcquireCtx(ctx)
   308  	}()
   309  
   310  	select {
   311  	case <-ready:
   312  		assert.Equal(float64(1.0), resources.Value())
   313  		assert.Zero(failures.Value())
   314  		cancel()
   315  	case <-time.After(time.Second):
   316  		assert.FailNow("Failed to spawn AcquireCtx goroutine")
   317  	}
   318  
   319  	select {
   320  	case err := <-result:
   321  		assert.Equal(ctx.Err(), err)
   322  		assert.Equal(float64(1.0), resources.Value())
   323  		assert.Equal(float64(1.0), failures.Value())
   324  
   325  		s.Release()
   326  		assert.Zero(resources.Value())
   327  		assert.Equal(float64(1.0), failures.Value())
   328  	case <-time.After(time.Second):
   329  		assert.FailNow("AcquireCtx blocked unexpectedly")
   330  	}
   331  }
   332  
   333  func TestInstrumentedSemaphore(t *testing.T) {
   334  	t.Run("Acquire", func(t *testing.T) {
   335  		t.Run("Success", testInstrumentedSemaphoreAcquireSuccess)
   336  		t.Run("Fail", testInstrumentedSemaphoreAcquireFail)
   337  	})
   338  
   339  	t.Run("TryAcquire", testInstrumentedSemaphoreTryAcquire)
   340  
   341  	t.Run("AcquireWait", func(t *testing.T) {
   342  		t.Run("Success", testInstrumentedSemaphoreAcquireWaitSuccess)
   343  		t.Run("Timeout", testInstrumentedSemaphoreAcquireWaitTimeout)
   344  	})
   345  
   346  	t.Run("AcquireCtx", func(t *testing.T) {
   347  		t.Run("Success", testInstrumentedSemaphoreAcquireCtxSuccess)
   348  		t.Run("Cancel", testInstrumentedSemaphoreAcquireCtxCancel)
   349  	})
   350  }
   351  
   352  func testInstrumentedCloseableAcquire(t *testing.T) {
   353  	var (
   354  		assert    = assert.New(t)
   355  		resources = generic.NewCounter("test")
   356  		failures  = generic.NewCounter("test")
   357  		closed    = generic.NewGauge("test")
   358  		s         = InstrumentCloseable(CloseableMutex(), WithResources(resources), WithFailures(failures), WithClosed(closed))
   359  
   360  		result = make(chan error)
   361  	)
   362  
   363  	assert.NotNil(s.Closed())
   364  	assert.Equal(MetricOpen, closed.Value())
   365  
   366  	go func() {
   367  		result <- s.Acquire()
   368  	}()
   369  
   370  	select {
   371  	case err := <-result:
   372  		assert.NoError(err)
   373  		assert.Equal(float64(1.0), resources.Value())
   374  		assert.Zero(failures.Value())
   375  		assert.Equal(MetricOpen, closed.Value())
   376  
   377  		assert.NoError(s.Release())
   378  		assert.Zero(resources.Value())
   379  		assert.Zero(failures.Value())
   380  		assert.Equal(MetricOpen, closed.Value())
   381  	case <-time.After(time.Second):
   382  		assert.FailNow("Acquire blocked unexpectedly")
   383  	}
   384  
   385  	assert.NoError(s.Close())
   386  	assert.Zero(resources.Value())
   387  	assert.Zero(failures.Value())
   388  	assert.Equal(MetricClosed, closed.Value())
   389  
   390  	assert.Equal(ErrClosed, s.Acquire())
   391  	assert.Zero(resources.Value())
   392  	assert.Equal(float64(1.0), failures.Value())
   393  	assert.Equal(MetricClosed, closed.Value())
   394  
   395  	select {
   396  	case <-s.Closed():
   397  		// passing
   398  	default:
   399  		assert.Fail("The Closed channel was not signaled")
   400  	}
   401  }
   402  
   403  func testInstrumentedCloseableTryAcquire(t *testing.T) {
   404  	var (
   405  		assert    = assert.New(t)
   406  		require   = require.New(t)
   407  		resources = generic.NewCounter("test")
   408  		failures  = generic.NewCounter("test")
   409  		closed    = generic.NewGauge("test")
   410  		s         = InstrumentCloseable(CloseableMutex(), WithResources(resources), WithFailures(failures), WithClosed(closed))
   411  	)
   412  
   413  	assert.Zero(resources.Value())
   414  	assert.Zero(failures.Value())
   415  	assert.Equal(MetricOpen, closed.Value())
   416  
   417  	require.True(s.TryAcquire())
   418  	assert.Equal(float64(1.0), resources.Value())
   419  	assert.Zero(failures.Value())
   420  	assert.Equal(MetricOpen, closed.Value())
   421  
   422  	require.False(s.TryAcquire())
   423  	assert.Equal(float64(1.0), resources.Value())
   424  	assert.Equal(float64(1.0), failures.Value())
   425  	assert.Equal(MetricOpen, closed.Value())
   426  
   427  	assert.NoError(s.Release())
   428  	assert.Zero(resources.Value())
   429  	assert.Equal(float64(1.0), failures.Value())
   430  	assert.Equal(MetricOpen, closed.Value())
   431  
   432  	assert.NoError(s.Close())
   433  	assert.Zero(resources.Value())
   434  	assert.Equal(float64(1.0), failures.Value())
   435  	assert.Equal(MetricClosed, closed.Value())
   436  
   437  	assert.False(s.TryAcquire())
   438  	assert.Zero(resources.Value())
   439  	assert.Equal(float64(2.0), failures.Value())
   440  	assert.Equal(MetricClosed, closed.Value())
   441  
   442  	select {
   443  	case <-s.Closed():
   444  		// passing
   445  	default:
   446  		assert.Fail("The Closed channel was not signaled")
   447  	}
   448  }
   449  
   450  func testInstrumentedCloseableAcquireWaitSuccess(t *testing.T) {
   451  	var (
   452  		assert    = assert.New(t)
   453  		resources = generic.NewCounter("test")
   454  		failures  = generic.NewCounter("test")
   455  		closed    = generic.NewGauge("test")
   456  		s         = InstrumentCloseable(CloseableMutex(), WithResources(resources), WithFailures(failures), WithClosed(closed))
   457  
   458  		ready  = make(chan struct{})
   459  		result = make(chan error)
   460  		timer  = make(chan time.Time)
   461  	)
   462  
   463  	assert.Equal(MetricOpen, closed.Value())
   464  
   465  	go func() {
   466  		s.Acquire()
   467  		close(ready)
   468  		result <- s.AcquireWait(timer)
   469  	}()
   470  
   471  	select {
   472  	case <-ready:
   473  		assert.Equal(float64(1.0), resources.Value())
   474  		assert.Zero(failures.Value())
   475  		assert.Equal(MetricOpen, closed.Value())
   476  		s.Release()
   477  	case <-time.After(time.Second):
   478  		assert.FailNow("Failed to spawn AcquireWait goroutine")
   479  	}
   480  
   481  	select {
   482  	case err := <-result:
   483  		assert.NoError(err)
   484  		assert.Equal(float64(1.0), resources.Value())
   485  		assert.Zero(failures.Value())
   486  		assert.Equal(MetricOpen, closed.Value())
   487  
   488  		assert.NoError(s.Release())
   489  		assert.Zero(resources.Value())
   490  		assert.Zero(failures.Value())
   491  		assert.Equal(MetricOpen, closed.Value())
   492  	case <-time.After(time.Second):
   493  		assert.FailNow("AcquireWait blocked unexpectedly")
   494  	}
   495  
   496  	assert.NoError(s.Close())
   497  	assert.Zero(resources.Value())
   498  	assert.Zero(failures.Value())
   499  	assert.Equal(MetricClosed, closed.Value())
   500  
   501  	select {
   502  	case <-s.Closed():
   503  		// passing
   504  	default:
   505  		assert.Fail("The Closed channel was not signaled")
   506  	}
   507  }
   508  
   509  func testInstrumentedCloseableAcquireWaitTimeout(t *testing.T) {
   510  	var (
   511  		assert    = assert.New(t)
   512  		resources = generic.NewCounter("test")
   513  		failures  = generic.NewCounter("test")
   514  		closed    = generic.NewGauge("test")
   515  		s         = InstrumentCloseable(CloseableMutex(), WithResources(resources), WithFailures(failures), WithClosed(closed))
   516  
   517  		ready  = make(chan struct{})
   518  		result = make(chan error)
   519  		timer  = make(chan time.Time)
   520  	)
   521  
   522  	assert.NotNil(s.Closed())
   523  	assert.Equal(MetricOpen, closed.Value())
   524  
   525  	go func() {
   526  		s.Acquire()
   527  		close(ready)
   528  		result <- s.AcquireWait(timer)
   529  	}()
   530  
   531  	select {
   532  	case <-ready:
   533  		assert.Equal(float64(1.0), resources.Value())
   534  		assert.Zero(failures.Value())
   535  		assert.Equal(MetricOpen, closed.Value())
   536  		timer <- time.Time{}
   537  	case <-time.After(time.Second):
   538  		assert.FailNow("Failed to spawn AcquireWait goroutine")
   539  	}
   540  
   541  	select {
   542  	case err := <-result:
   543  		assert.Equal(ErrTimeout, err)
   544  		assert.Equal(float64(1.0), resources.Value())
   545  		assert.Equal(float64(1.0), failures.Value())
   546  		assert.Equal(MetricOpen, closed.Value())
   547  
   548  		s.Release()
   549  		assert.Zero(resources.Value())
   550  		assert.Equal(float64(1.0), failures.Value())
   551  		assert.Equal(MetricOpen, closed.Value())
   552  	case <-time.After(time.Second):
   553  		assert.FailNow("AcquireWait blocked unexpectedly")
   554  	}
   555  
   556  	assert.NoError(s.Close())
   557  	assert.Zero(resources.Value())
   558  	assert.Equal(float64(1.0), failures.Value())
   559  	assert.Equal(MetricClosed, closed.Value())
   560  
   561  	select {
   562  	case <-s.Closed():
   563  		// passing
   564  	default:
   565  		assert.Fail("The Closed channel was not signaled")
   566  	}
   567  }
   568  
   569  func testInstrumentedCloseableAcquireWaitClose(t *testing.T) {
   570  	var (
   571  		assert    = assert.New(t)
   572  		resources = generic.NewCounter("test")
   573  		failures  = generic.NewCounter("test")
   574  		closed    = generic.NewGauge("test")
   575  		s         = InstrumentCloseable(CloseableMutex(), WithResources(resources), WithFailures(failures), WithClosed(closed))
   576  
   577  		ready  = make(chan struct{})
   578  		result = make(chan error)
   579  		timer  = make(chan time.Time)
   580  	)
   581  
   582  	assert.NotNil(s.Closed())
   583  	assert.Equal(MetricOpen, closed.Value())
   584  
   585  	go func() {
   586  		s.Acquire()
   587  		close(ready)
   588  		result <- s.AcquireWait(timer)
   589  	}()
   590  
   591  	select {
   592  	case <-ready:
   593  		assert.Equal(float64(1.0), resources.Value())
   594  		assert.Zero(failures.Value())
   595  		assert.Equal(MetricOpen, closed.Value())
   596  		assert.NoError(s.Close())
   597  	case <-time.After(time.Second):
   598  		assert.FailNow("Failed to spawn AcquireWait goroutine")
   599  	}
   600  
   601  	select {
   602  	case err := <-result:
   603  		assert.Equal(ErrClosed, err)
   604  		assert.Equal(float64(1.0), resources.Value())
   605  		assert.Equal(float64(1.0), failures.Value())
   606  		assert.Equal(MetricClosed, closed.Value())
   607  
   608  		s.Release()
   609  		assert.Equal(float64(1.0), resources.Value())
   610  		assert.Equal(float64(1.0), failures.Value())
   611  		assert.Equal(MetricClosed, closed.Value())
   612  	case <-time.After(time.Second):
   613  		assert.FailNow("AcquireWait blocked unexpectedly")
   614  	}
   615  
   616  	assert.Equal(ErrClosed, s.Close())
   617  	assert.Equal(float64(1.0), resources.Value())
   618  	assert.Equal(float64(1.0), failures.Value())
   619  	assert.Equal(MetricClosed, closed.Value())
   620  
   621  	select {
   622  	case <-s.Closed():
   623  		// passing
   624  	default:
   625  		assert.Fail("The Closed channel was not signaled")
   626  	}
   627  
   628  	assert.Equal(ErrClosed, s.AcquireWait(timer))
   629  	assert.Equal(float64(1.0), resources.Value())
   630  	assert.Equal(float64(2.0), failures.Value())
   631  	assert.Equal(MetricClosed, closed.Value())
   632  }
   633  
   634  func testInstrumentedCloseableAcquireCtxSuccess(t *testing.T) {
   635  	var (
   636  		assert    = assert.New(t)
   637  		resources = generic.NewCounter("test")
   638  		failures  = generic.NewCounter("test")
   639  		closed    = generic.NewGauge("test")
   640  		s         = InstrumentCloseable(CloseableMutex(), WithResources(resources), WithFailures(failures), WithClosed(closed))
   641  
   642  		ready       = make(chan struct{})
   643  		result      = make(chan error)
   644  		ctx, cancel = context.WithCancel(context.Background())
   645  	)
   646  
   647  	defer cancel()
   648  	assert.NotNil(s.Closed())
   649  	assert.Equal(MetricOpen, closed.Value())
   650  
   651  	go func() {
   652  		s.Acquire()
   653  		close(ready)
   654  		result <- s.AcquireCtx(ctx)
   655  	}()
   656  
   657  	select {
   658  	case <-ready:
   659  		assert.Equal(float64(1.0), resources.Value())
   660  		assert.Zero(failures.Value())
   661  		assert.Equal(MetricOpen, closed.Value())
   662  		s.Release()
   663  
   664  		assert.Zero(resources.Value())
   665  		assert.Zero(failures.Value())
   666  		assert.Equal(MetricOpen, closed.Value())
   667  	case <-time.After(time.Second):
   668  		assert.FailNow("Failed to spawn AcquireCtx goroutine")
   669  	}
   670  
   671  	select {
   672  	case err := <-result:
   673  		assert.NoError(err)
   674  		assert.Equal(float64(1.0), resources.Value())
   675  		assert.Zero(failures.Value())
   676  		assert.Equal(MetricOpen, closed.Value())
   677  
   678  		s.Release()
   679  		assert.Zero(resources.Value())
   680  		assert.Zero(failures.Value())
   681  		assert.Equal(MetricOpen, closed.Value())
   682  	case <-time.After(time.Second):
   683  		assert.FailNow("AcquireCtx blocked unexpectedly")
   684  	}
   685  
   686  	assert.NoError(s.Close())
   687  	assert.Zero(resources.Value())
   688  	assert.Zero(failures.Value())
   689  	assert.Equal(MetricClosed, closed.Value())
   690  
   691  	select {
   692  	case <-s.Closed():
   693  		// passing
   694  	default:
   695  		assert.Fail("The Closed channel was not signaled")
   696  	}
   697  
   698  	assert.Equal(ErrClosed, s.AcquireCtx(ctx))
   699  	assert.Zero(resources.Value())
   700  	assert.Equal(float64(1.0), failures.Value())
   701  	assert.Equal(MetricClosed, closed.Value())
   702  }
   703  
   704  func testInstrumentedCloseableAcquireCtxCancel(t *testing.T) {
   705  	var (
   706  		assert    = assert.New(t)
   707  		resources = generic.NewCounter("test")
   708  		failures  = generic.NewCounter("test")
   709  		closed    = generic.NewGauge("test")
   710  		s         = InstrumentCloseable(CloseableMutex(), WithResources(resources), WithFailures(failures), WithClosed(closed))
   711  
   712  		ready       = make(chan struct{})
   713  		result      = make(chan error)
   714  		ctx, cancel = context.WithCancel(context.Background())
   715  	)
   716  
   717  	defer cancel()
   718  	assert.NotNil(s.Closed())
   719  	assert.Equal(MetricOpen, closed.Value())
   720  
   721  	go func() {
   722  		s.Acquire()
   723  		close(ready)
   724  		result <- s.AcquireCtx(ctx)
   725  	}()
   726  
   727  	select {
   728  	case <-ready:
   729  		assert.Equal(float64(1.0), resources.Value())
   730  		assert.Zero(failures.Value())
   731  		assert.Equal(MetricOpen, closed.Value())
   732  		cancel()
   733  	case <-time.After(time.Second):
   734  		assert.FailNow("Failed to spawn AcquireCtx goroutine")
   735  	}
   736  
   737  	select {
   738  	case err := <-result:
   739  		assert.Equal(ctx.Err(), err)
   740  		assert.Equal(float64(1.0), resources.Value())
   741  		assert.Equal(float64(1.0), failures.Value())
   742  		assert.Equal(MetricOpen, closed.Value())
   743  
   744  		s.Release()
   745  		assert.Zero(resources.Value())
   746  		assert.Equal(float64(1.0), failures.Value())
   747  		assert.Equal(MetricOpen, closed.Value())
   748  	case <-time.After(time.Second):
   749  		assert.FailNow("AcquireCtx blocked unexpectedly")
   750  	}
   751  
   752  	assert.NoError(s.Close())
   753  	assert.Zero(resources.Value())
   754  	assert.Equal(float64(1.0), failures.Value())
   755  	assert.Equal(MetricClosed, closed.Value())
   756  
   757  	select {
   758  	case <-s.Closed():
   759  		// passing
   760  	default:
   761  		assert.Fail("The Closed channel was not signaled")
   762  	}
   763  }
   764  
   765  func testInstrumentedCloseableAcquireCtxClose(t *testing.T) {
   766  	var (
   767  		assert    = assert.New(t)
   768  		resources = generic.NewCounter("test")
   769  		failures  = generic.NewCounter("test")
   770  		closed    = generic.NewGauge("test")
   771  		s         = InstrumentCloseable(CloseableMutex(), WithResources(resources), WithFailures(failures), WithClosed(closed))
   772  
   773  		ready       = make(chan struct{})
   774  		result      = make(chan error)
   775  		ctx, cancel = context.WithCancel(context.Background())
   776  	)
   777  
   778  	defer cancel()
   779  	assert.NotNil(s.Closed())
   780  	assert.Equal(MetricOpen, closed.Value())
   781  
   782  	go func() {
   783  		s.Acquire()
   784  		close(ready)
   785  		result <- s.AcquireCtx(ctx)
   786  	}()
   787  
   788  	select {
   789  	case <-ready:
   790  		assert.Equal(float64(1.0), resources.Value())
   791  		assert.Zero(failures.Value())
   792  		assert.Equal(MetricOpen, closed.Value())
   793  
   794  		assert.NoError(s.Close())
   795  		assert.Equal(float64(1.0), resources.Value())
   796  		assert.Zero(failures.Value())
   797  		assert.Equal(MetricClosed, closed.Value())
   798  	case <-time.After(time.Second):
   799  		assert.FailNow("Failed to spawn AcquireCtx goroutine")
   800  	}
   801  
   802  	select {
   803  	case err := <-result:
   804  		assert.Equal(ErrClosed, err)
   805  		assert.Equal(float64(1.0), resources.Value())
   806  		assert.Equal(float64(1.0), failures.Value())
   807  		assert.Equal(MetricClosed, closed.Value())
   808  
   809  		assert.Equal(ErrClosed, s.Release())
   810  		assert.Equal(float64(1.0), resources.Value())
   811  		assert.Equal(float64(1.0), failures.Value())
   812  		assert.Equal(MetricClosed, closed.Value())
   813  	case <-time.After(time.Second):
   814  		assert.FailNow("AcquireCtx blocked unexpectedly")
   815  	}
   816  
   817  	assert.Equal(ErrClosed, s.Close())
   818  	assert.Equal(float64(1.0), resources.Value())
   819  	assert.Equal(float64(1.0), failures.Value())
   820  	assert.Equal(MetricClosed, closed.Value())
   821  
   822  	select {
   823  	case <-s.Closed():
   824  		// passing
   825  	default:
   826  		assert.Fail("The Closed channel was not signaled")
   827  	}
   828  }
   829  
   830  func TestInstrumentedCloseable(t *testing.T) {
   831  	t.Run("Acquire", testInstrumentedCloseableAcquire)
   832  
   833  	t.Run("TryAcquire", testInstrumentedCloseableTryAcquire)
   834  
   835  	t.Run("AcquireWait", func(t *testing.T) {
   836  		t.Run("Success", testInstrumentedCloseableAcquireWaitSuccess)
   837  		t.Run("Timeout", testInstrumentedCloseableAcquireWaitTimeout)
   838  		t.Run("Close", testInstrumentedCloseableAcquireWaitClose)
   839  	})
   840  
   841  	t.Run("AcquireCtx", func(t *testing.T) {
   842  		t.Run("Success", testInstrumentedCloseableAcquireCtxSuccess)
   843  		t.Run("Cancel", testInstrumentedCloseableAcquireCtxCancel)
   844  		t.Run("Close", testInstrumentedCloseableAcquireCtxClose)
   845  	})
   846  }