github.com/timandy/routine@v1.1.4-0.20240507073150-e4a3e1fe2ba5/api_routine_test.go (about)

     1  package routine
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"path"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  func TestRunnable(t *testing.T) {
    17  	count := 0
    18  	var runnable Runnable = func() {
    19  		count++
    20  	}
    21  	runnable()
    22  	assert.Equal(t, 1, count)
    23  	//
    24  	var fun func() = runnable
    25  	fun()
    26  	assert.Equal(t, 2, count)
    27  }
    28  
    29  func TestCallable(t *testing.T) {
    30  	var callable Callable[string] = func() string {
    31  		return "Hello"
    32  	}
    33  	assert.Equal(t, "Hello", callable())
    34  	//
    35  	var fun func() string = callable
    36  	assert.Equal(t, "Hello", fun())
    37  }
    38  
    39  func TestCancelRunnable(t *testing.T) {
    40  	count := 0
    41  	var cancelRunnable CancelRunnable = func(token CancelToken) {
    42  		count++
    43  	}
    44  	cancelRunnable(nil)
    45  	assert.Equal(t, 1, count)
    46  	//
    47  	var fun func(CancelToken) = cancelRunnable
    48  	fun(nil)
    49  	assert.Equal(t, 2, count)
    50  }
    51  
    52  func TestCancelCallable(t *testing.T) {
    53  	var cancelCallable CancelCallable[string] = func(token CancelToken) string {
    54  		return "Hello"
    55  	}
    56  	assert.Equal(t, "Hello", cancelCallable(nil))
    57  	//
    58  	var fun func(CancelToken) string = cancelCallable
    59  	assert.Equal(t, "Hello", fun(nil))
    60  }
    61  
    62  func TestWrapTask_NoContext(t *testing.T) {
    63  	run := false
    64  	wrappedRun := false
    65  	wg := &sync.WaitGroup{}
    66  	wg.Add(1)
    67  	tls := NewThreadLocal[string]()
    68  	tlsInherit := NewInheritableThreadLocal[string]()
    69  	tls.Set("hello")
    70  	tlsInherit.Set("inherit hello")
    71  	assert.Equal(t, "hello", tls.Get())
    72  	assert.Equal(t, "inherit hello", tlsInherit.Get())
    73  	task := WrapTask(func() {
    74  		assert.Equal(t, "", tls.Get())
    75  		assert.Equal(t, "inherit hello", tlsInherit.Get())
    76  		tls.Set("世界")
    77  		tlsInherit.Set("inherit 世界")
    78  		assert.Equal(t, "世界", tls.Get())
    79  		assert.Equal(t, "inherit 世界", tlsInherit.Get())
    80  		wrappedRun = true
    81  	})
    82  	tls.Set("world")
    83  	tlsInherit.Set("inherit world")
    84  	assert.Equal(t, "world", tls.Get())
    85  	assert.Equal(t, "inherit world", tlsInherit.Get())
    86  	go func() {
    87  		task.Run()
    88  		assert.Equal(t, "", tls.Get())
    89  		assert.Equal(t, "", tlsInherit.Get())
    90  		run = true
    91  		wg.Done()
    92  	}()
    93  	assert.Equal(t, "world", tls.Get())
    94  	assert.Equal(t, "inherit world", tlsInherit.Get())
    95  	wg.Wait()
    96  	assert.True(t, wrappedRun)
    97  	assert.True(t, run)
    98  }
    99  
   100  func TestWrapTask_HasContext(t *testing.T) {
   101  	run := false
   102  	wrappedRun := false
   103  	wg := &sync.WaitGroup{}
   104  	wg.Add(1)
   105  	tls := NewThreadLocal[string]()
   106  	tlsInherit := NewInheritableThreadLocal[string]()
   107  	tls.Set("hello")
   108  	tlsInherit.Set("inherit hello")
   109  	assert.Equal(t, "hello", tls.Get())
   110  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   111  	task := WrapTask(func() {
   112  		assert.Equal(t, "", tls.Get())
   113  		assert.Equal(t, "inherit hello", tlsInherit.Get())
   114  		tls.Set("世界")
   115  		tlsInherit.Set("inherit 世界")
   116  		assert.Equal(t, "世界", tls.Get())
   117  		assert.Equal(t, "inherit 世界", tlsInherit.Get())
   118  		wrappedRun = true
   119  	})
   120  	tls.Set("world")
   121  	tlsInherit.Set("inherit world")
   122  	assert.Equal(t, "world", tls.Get())
   123  	assert.Equal(t, "inherit world", tlsInherit.Get())
   124  	go func() {
   125  		tls.Set("你好")
   126  		tlsInherit.Set("inherit 你好")
   127  		task.Run()
   128  		assert.Equal(t, "你好", tls.Get())
   129  		assert.Equal(t, "inherit 你好", tlsInherit.Get())
   130  		run = true
   131  		wg.Done()
   132  	}()
   133  	assert.Equal(t, "world", tls.Get())
   134  	assert.Equal(t, "inherit world", tlsInherit.Get())
   135  	wg.Wait()
   136  	assert.True(t, wrappedRun)
   137  	assert.True(t, run)
   138  }
   139  
   140  func TestWrapTask_Complete_ThenFail(t *testing.T) {
   141  	newStdout, oldStdout := captureStdout()
   142  	defer restoreStdout(newStdout, oldStdout)
   143  	//
   144  	run := false
   145  	wg := &sync.WaitGroup{}
   146  	wg.Add(1)
   147  	wg2 := &sync.WaitGroup{}
   148  	wg2.Add(1)
   149  	wg3 := &sync.WaitGroup{}
   150  	wg3.Add(1)
   151  	task := WrapTask(func() {
   152  		wg.Done()  //1
   153  		wg2.Wait() //4
   154  		run = true
   155  		wg3.Done() //5
   156  		panic(1)
   157  	})
   158  	go task.Run()
   159  	wg.Wait() //2
   160  	task.Complete(nil)
   161  	assert.Nil(t, task.Get())
   162  	wg2.Done() //3
   163  	wg3.Wait() //6
   164  	assert.True(t, task.IsDone())
   165  	assert.False(t, task.IsFailed())
   166  	assert.False(t, task.IsCanceled())
   167  	assert.True(t, run)
   168  	//
   169  	time.Sleep(10 * time.Millisecond)
   170  	output := readAll(newStdout)
   171  	assert.Equal(t, "", output)
   172  }
   173  
   174  func TestWrapWaitTask_NoContext(t *testing.T) {
   175  	run := false
   176  	wrappedRun := false
   177  	wg := &sync.WaitGroup{}
   178  	wg.Add(1)
   179  	tls := NewThreadLocal[string]()
   180  	tlsInherit := NewInheritableThreadLocal[string]()
   181  	tls.Set("hello")
   182  	tlsInherit.Set("inherit hello")
   183  	assert.Equal(t, "hello", tls.Get())
   184  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   185  	task := WrapWaitTask(func(token CancelToken) {
   186  		assert.Equal(t, "", tls.Get())
   187  		assert.Equal(t, "inherit hello", tlsInherit.Get())
   188  		tls.Set("世界")
   189  		tlsInherit.Set("inherit 世界")
   190  		assert.Equal(t, "世界", tls.Get())
   191  		assert.Equal(t, "inherit 世界", tlsInherit.Get())
   192  		wrappedRun = true
   193  	})
   194  	tls.Set("world")
   195  	tlsInherit.Set("inherit world")
   196  	assert.Equal(t, "world", tls.Get())
   197  	assert.Equal(t, "inherit world", tlsInherit.Get())
   198  	go func() {
   199  		task.Run()
   200  		assert.Equal(t, "", tls.Get())
   201  		assert.Equal(t, "", tlsInherit.Get())
   202  		run = true
   203  		wg.Done()
   204  	}()
   205  	assert.Equal(t, "world", tls.Get())
   206  	assert.Equal(t, "inherit world", tlsInherit.Get())
   207  	assert.Nil(t, task.Get())
   208  	assert.True(t, wrappedRun)
   209  	wg.Wait()
   210  	assert.True(t, wrappedRun)
   211  	assert.True(t, run)
   212  }
   213  
   214  func TestWrapWaitTask_NoContext_Cancel(t *testing.T) {
   215  	run := false
   216  	wrappedRun := false
   217  	wg := &sync.WaitGroup{}
   218  	wg.Add(1)
   219  	tls := NewThreadLocal[string]()
   220  	tlsInherit := NewInheritableThreadLocal[string]()
   221  	tls.Set("hello")
   222  	tlsInherit.Set("inherit hello")
   223  	assert.Equal(t, "hello", tls.Get())
   224  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   225  	task := WrapWaitTask(func(token CancelToken) {
   226  		for i := 0; i < 1000; i++ {
   227  			if token.IsCanceled() {
   228  				return
   229  			}
   230  			time.Sleep(1 * time.Millisecond)
   231  		}
   232  		wrappedRun = true
   233  	})
   234  	tls.Set("world")
   235  	tlsInherit.Set("inherit world")
   236  	assert.Equal(t, "world", tls.Get())
   237  	assert.Equal(t, "inherit world", tlsInherit.Get())
   238  	go func() {
   239  		task.Run()
   240  		assert.Equal(t, "", tls.Get())
   241  		assert.Equal(t, "", tlsInherit.Get())
   242  		run = true
   243  		wg.Done()
   244  	}()
   245  	assert.Equal(t, "world", tls.Get())
   246  	assert.Equal(t, "inherit world", tlsInherit.Get())
   247  	task.Cancel()
   248  	assert.True(t, task.IsCanceled())
   249  	assert.Panics(t, func() {
   250  		task.Get()
   251  	})
   252  	assert.False(t, wrappedRun)
   253  	wg.Wait()
   254  	assert.False(t, wrappedRun)
   255  	assert.True(t, run)
   256  }
   257  
   258  func TestWrapWaitTask_HasContext(t *testing.T) {
   259  	run := false
   260  	wrappedRun := false
   261  	wg := &sync.WaitGroup{}
   262  	wg.Add(1)
   263  	tls := NewThreadLocal[string]()
   264  	tlsInherit := NewInheritableThreadLocal[string]()
   265  	tls.Set("hello")
   266  	tlsInherit.Set("inherit hello")
   267  	assert.Equal(t, "hello", tls.Get())
   268  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   269  	task := WrapWaitTask(func(token CancelToken) {
   270  		assert.Equal(t, "", tls.Get())
   271  		assert.Equal(t, "inherit hello", tlsInherit.Get())
   272  		tls.Set("世界")
   273  		tlsInherit.Set("inherit 世界")
   274  		assert.Equal(t, "世界", tls.Get())
   275  		assert.Equal(t, "inherit 世界", tlsInherit.Get())
   276  		wrappedRun = true
   277  	})
   278  	tls.Set("world")
   279  	tlsInherit.Set("inherit world")
   280  	assert.Equal(t, "world", tls.Get())
   281  	assert.Equal(t, "inherit world", tlsInherit.Get())
   282  	go func() {
   283  		tls.Set("你好")
   284  		tlsInherit.Set("inherit 你好")
   285  		task.Run()
   286  		assert.Equal(t, "你好", tls.Get())
   287  		assert.Equal(t, "inherit 你好", tlsInherit.Get())
   288  		run = true
   289  		wg.Done()
   290  	}()
   291  	assert.Equal(t, "world", tls.Get())
   292  	assert.Equal(t, "inherit world", tlsInherit.Get())
   293  	assert.Nil(t, task.Get())
   294  	assert.True(t, wrappedRun)
   295  	wg.Wait()
   296  	assert.True(t, wrappedRun)
   297  	assert.True(t, run)
   298  }
   299  
   300  func TestWrapWaitTask_HasContext_Cancel(t *testing.T) {
   301  	run := false
   302  	wrappedRun := false
   303  	wg := &sync.WaitGroup{}
   304  	wg.Add(1)
   305  	tls := NewThreadLocal[string]()
   306  	tlsInherit := NewInheritableThreadLocal[string]()
   307  	tls.Set("hello")
   308  	tlsInherit.Set("inherit hello")
   309  	assert.Equal(t, "hello", tls.Get())
   310  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   311  	task := WrapWaitTask(func(token CancelToken) {
   312  		for i := 0; i < 1000; i++ {
   313  			if token.IsCanceled() {
   314  				return
   315  			}
   316  			time.Sleep(1 * time.Millisecond)
   317  		}
   318  		wrappedRun = true
   319  	})
   320  	tls.Set("world")
   321  	tlsInherit.Set("inherit world")
   322  	assert.Equal(t, "world", tls.Get())
   323  	assert.Equal(t, "inherit world", tlsInherit.Get())
   324  	go func() {
   325  		tls.Set("你好")
   326  		tlsInherit.Set("inherit 你好")
   327  		task.Run()
   328  		assert.Equal(t, "你好", tls.Get())
   329  		assert.Equal(t, "inherit 你好", tlsInherit.Get())
   330  		run = true
   331  		wg.Done()
   332  	}()
   333  	assert.Equal(t, "world", tls.Get())
   334  	assert.Equal(t, "inherit world", tlsInherit.Get())
   335  	task.Cancel()
   336  	assert.True(t, task.IsCanceled())
   337  	assert.Panics(t, func() {
   338  		task.Get()
   339  	})
   340  	assert.False(t, wrappedRun)
   341  	wg.Wait()
   342  	assert.False(t, wrappedRun)
   343  	assert.True(t, run)
   344  }
   345  
   346  func TestWrapWaitTask_Complete_ThenFail(t *testing.T) {
   347  	newStdout, oldStdout := captureStdout()
   348  	defer restoreStdout(newStdout, oldStdout)
   349  	//
   350  	run := false
   351  	wg := &sync.WaitGroup{}
   352  	wg.Add(1)
   353  	wg2 := &sync.WaitGroup{}
   354  	wg2.Add(1)
   355  	wg3 := &sync.WaitGroup{}
   356  	wg3.Add(1)
   357  	task := WrapWaitTask(func(token CancelToken) {
   358  		wg.Done()  //1
   359  		wg2.Wait() //4
   360  		run = true
   361  		wg3.Done() //5
   362  		panic(1)
   363  	})
   364  	go task.Run()
   365  	wg.Wait() //2
   366  	task.Complete(nil)
   367  	assert.Nil(t, task.Get())
   368  	wg2.Done() //3
   369  	wg3.Wait() //6
   370  	assert.True(t, task.IsDone())
   371  	assert.False(t, task.IsFailed())
   372  	assert.False(t, task.IsCanceled())
   373  	assert.True(t, run)
   374  	//
   375  	time.Sleep(10 * time.Millisecond)
   376  	output := readAll(newStdout)
   377  	assert.Equal(t, "", output)
   378  }
   379  
   380  func TestWrapWaitResultTask_NoContext(t *testing.T) {
   381  	run := false
   382  	wrappedRun := false
   383  	wg := &sync.WaitGroup{}
   384  	wg.Add(1)
   385  	tls := NewThreadLocal[string]()
   386  	tlsInherit := NewInheritableThreadLocal[string]()
   387  	tls.Set("hello")
   388  	tlsInherit.Set("inherit hello")
   389  	assert.Equal(t, "hello", tls.Get())
   390  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   391  	task := WrapWaitResultTask(func(token CancelToken) any {
   392  		assert.Equal(t, "", tls.Get())
   393  		assert.Equal(t, "inherit hello", tlsInherit.Get())
   394  		tls.Set("世界")
   395  		tlsInherit.Set("inherit 世界")
   396  		assert.Equal(t, "世界", tls.Get())
   397  		assert.Equal(t, "inherit 世界", tlsInherit.Get())
   398  		wrappedRun = true
   399  		return 1
   400  	})
   401  	tls.Set("world")
   402  	tlsInherit.Set("inherit world")
   403  	assert.Equal(t, "world", tls.Get())
   404  	assert.Equal(t, "inherit world", tlsInherit.Get())
   405  	go func() {
   406  		task.Run()
   407  		assert.Equal(t, "", tls.Get())
   408  		assert.Equal(t, "", tlsInherit.Get())
   409  		run = true
   410  		wg.Done()
   411  	}()
   412  	assert.Equal(t, "world", tls.Get())
   413  	assert.Equal(t, "inherit world", tlsInherit.Get())
   414  	assert.Equal(t, 1, task.Get())
   415  	assert.True(t, wrappedRun)
   416  	wg.Wait()
   417  	assert.True(t, wrappedRun)
   418  	assert.True(t, run)
   419  }
   420  
   421  func TestWrapWaitResultTask_NoContext_Cancel(t *testing.T) {
   422  	run := false
   423  	wrappedRun := false
   424  	wg := &sync.WaitGroup{}
   425  	wg.Add(1)
   426  	tls := NewThreadLocal[string]()
   427  	tlsInherit := NewInheritableThreadLocal[string]()
   428  	tls.Set("hello")
   429  	tlsInherit.Set("inherit hello")
   430  	assert.Equal(t, "hello", tls.Get())
   431  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   432  	task := WrapWaitResultTask(func(token CancelToken) any {
   433  		for i := 0; i < 1000; i++ {
   434  			if token.IsCanceled() {
   435  				return 0
   436  			}
   437  			time.Sleep(1 * time.Millisecond)
   438  		}
   439  		wrappedRun = true
   440  		return 1
   441  	})
   442  	tls.Set("world")
   443  	tlsInherit.Set("inherit world")
   444  	assert.Equal(t, "world", tls.Get())
   445  	assert.Equal(t, "inherit world", tlsInherit.Get())
   446  	go func() {
   447  		task.Run()
   448  		assert.Equal(t, "", tls.Get())
   449  		assert.Equal(t, "", tlsInherit.Get())
   450  		run = true
   451  		wg.Done()
   452  	}()
   453  	assert.Equal(t, "world", tls.Get())
   454  	assert.Equal(t, "inherit world", tlsInherit.Get())
   455  	task.Cancel()
   456  	assert.True(t, task.IsCanceled())
   457  	assert.Panics(t, func() {
   458  		task.Get()
   459  	})
   460  	assert.False(t, wrappedRun)
   461  	wg.Wait()
   462  	assert.False(t, wrappedRun)
   463  	assert.True(t, run)
   464  }
   465  
   466  func TestWrapWaitResultTask_HasContext(t *testing.T) {
   467  	run := false
   468  	wrappedRun := false
   469  	wg := &sync.WaitGroup{}
   470  	wg.Add(1)
   471  	tls := NewThreadLocal[string]()
   472  	tlsInherit := NewInheritableThreadLocal[string]()
   473  	tls.Set("hello")
   474  	tlsInherit.Set("inherit hello")
   475  	assert.Equal(t, "hello", tls.Get())
   476  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   477  	task := WrapWaitResultTask(func(token CancelToken) any {
   478  		assert.Equal(t, "", tls.Get())
   479  		assert.Equal(t, "inherit hello", tlsInherit.Get())
   480  		tls.Set("世界")
   481  		tlsInherit.Set("inherit 世界")
   482  		assert.Equal(t, "世界", tls.Get())
   483  		assert.Equal(t, "inherit 世界", tlsInherit.Get())
   484  		wrappedRun = true
   485  		return 1
   486  	})
   487  	tls.Set("world")
   488  	tlsInherit.Set("inherit world")
   489  	assert.Equal(t, "world", tls.Get())
   490  	assert.Equal(t, "inherit world", tlsInherit.Get())
   491  	go func() {
   492  		tls.Set("你好")
   493  		tlsInherit.Set("inherit 你好")
   494  		task.Run()
   495  		assert.Equal(t, "你好", tls.Get())
   496  		assert.Equal(t, "inherit 你好", tlsInherit.Get())
   497  		run = true
   498  		wg.Done()
   499  	}()
   500  	assert.Equal(t, "world", tls.Get())
   501  	assert.Equal(t, "inherit world", tlsInherit.Get())
   502  	assert.Equal(t, 1, task.Get())
   503  	assert.True(t, wrappedRun)
   504  	wg.Wait()
   505  	assert.True(t, wrappedRun)
   506  	assert.True(t, run)
   507  }
   508  
   509  func TestWrapWaitResultTask_HasContext_Cancel(t *testing.T) {
   510  	run := false
   511  	wrappedRun := false
   512  	wg := &sync.WaitGroup{}
   513  	wg.Add(1)
   514  	tls := NewThreadLocal[string]()
   515  	tlsInherit := NewInheritableThreadLocal[string]()
   516  	tls.Set("hello")
   517  	tlsInherit.Set("inherit hello")
   518  	assert.Equal(t, "hello", tls.Get())
   519  	assert.Equal(t, "inherit hello", tlsInherit.Get())
   520  	task := WrapWaitResultTask(func(token CancelToken) any {
   521  		for i := 0; i < 1000; i++ {
   522  			if token.IsCanceled() {
   523  				return 0
   524  			}
   525  			time.Sleep(1 * time.Millisecond)
   526  		}
   527  		wrappedRun = true
   528  		return 1
   529  	})
   530  	tls.Set("world")
   531  	tlsInherit.Set("inherit world")
   532  	assert.Equal(t, "world", tls.Get())
   533  	assert.Equal(t, "inherit world", tlsInherit.Get())
   534  	go func() {
   535  		tls.Set("你好")
   536  		tlsInherit.Set("inherit 你好")
   537  		task.Run()
   538  		assert.Equal(t, "你好", tls.Get())
   539  		assert.Equal(t, "inherit 你好", tlsInherit.Get())
   540  		run = true
   541  		wg.Done()
   542  	}()
   543  	assert.Equal(t, "world", tls.Get())
   544  	assert.Equal(t, "inherit world", tlsInherit.Get())
   545  	task.Cancel()
   546  	assert.True(t, task.IsCanceled())
   547  	assert.Panics(t, func() {
   548  		task.Get()
   549  	})
   550  	assert.False(t, wrappedRun)
   551  	wg.Wait()
   552  	assert.False(t, wrappedRun)
   553  	assert.True(t, run)
   554  }
   555  
   556  func TestWrapWaitResultTask_Complete_ThenFail(t *testing.T) {
   557  	newStdout, oldStdout := captureStdout()
   558  	defer restoreStdout(newStdout, oldStdout)
   559  	//
   560  	run := false
   561  	wg := &sync.WaitGroup{}
   562  	wg.Add(1)
   563  	wg2 := &sync.WaitGroup{}
   564  	wg2.Add(1)
   565  	wg3 := &sync.WaitGroup{}
   566  	wg3.Add(1)
   567  	task := WrapWaitResultTask(func(token CancelToken) any {
   568  		wg.Done()  //1
   569  		wg2.Wait() //4
   570  		run = true
   571  		wg3.Done() //5
   572  		panic(1)
   573  	})
   574  	go task.Run()
   575  	wg.Wait() //2
   576  	task.Complete(nil)
   577  	assert.Nil(t, task.Get())
   578  	wg2.Done() //3
   579  	wg3.Wait() //6
   580  	assert.True(t, task.IsDone())
   581  	assert.False(t, task.IsFailed())
   582  	assert.False(t, task.IsCanceled())
   583  	assert.True(t, run)
   584  	//
   585  	time.Sleep(10 * time.Millisecond)
   586  	output := readAll(newStdout)
   587  	assert.Equal(t, "", output)
   588  }
   589  
   590  func TestGo_Error(t *testing.T) {
   591  	newStdout, oldStdout := captureStdout()
   592  	defer restoreStdout(newStdout, oldStdout)
   593  	//
   594  	run := false
   595  	assert.NotPanics(t, func() {
   596  		wg := &sync.WaitGroup{}
   597  		wg.Add(1)
   598  		Go(func() {
   599  			run = true
   600  			wg.Done()
   601  			panic("error")
   602  		})
   603  		wg.Wait()
   604  	})
   605  	assert.True(t, run)
   606  	//
   607  	time.Sleep(10 * time.Millisecond)
   608  	output := readAll(newStdout)
   609  	lines := strings.Split(output, newLine)
   610  	assert.Equal(t, 7, len(lines))
   611  	//
   612  	line := lines[0]
   613  	assert.Equal(t, "RuntimeError: error", line)
   614  	//
   615  	line = lines[1]
   616  	assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.TestGo_Error."))
   617  	assert.True(t, strings.HasSuffix(line, "api_routine_test.go:601"))
   618  	//
   619  	line = lines[2]
   620  	assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.inheritedTask.run()"))
   621  	assert.True(t, strings.HasSuffix(line, "routine.go:31"))
   622  	//
   623  	line = lines[3]
   624  	assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.(*futureTask[...]).Run()"))
   625  	assert.True(t, strings.HasSuffix(line, "future_task.go:108"))
   626  	//
   627  	line = lines[4]
   628  	assert.Equal(t, "   --- End of error stack trace ---", line)
   629  	//
   630  	line = lines[5]
   631  	assert.True(t, strings.HasPrefix(line, "   created by github.com/timandy/routine.Go()"))
   632  	assert.True(t, strings.HasSuffix(line, "api_routine.go:49"))
   633  	//
   634  	line = lines[6]
   635  	assert.Equal(t, "", line)
   636  }
   637  
   638  func TestGo_Nil(t *testing.T) {
   639  	assert.Nil(t, createInheritedMap())
   640  	//
   641  	run := false
   642  	wg := &sync.WaitGroup{}
   643  	wg.Add(1)
   644  	Go(func() {
   645  		assert.Nil(t, createInheritedMap())
   646  		run = true
   647  		wg.Done()
   648  	})
   649  	wg.Wait()
   650  	assert.True(t, run)
   651  }
   652  
   653  func TestGo_Value(t *testing.T) {
   654  	tls := NewThreadLocal[string]()
   655  	tls.Set("Hello")
   656  	assert.Equal(t, "Hello", tls.Get())
   657  	//
   658  	inheritableTls := NewInheritableThreadLocal[string]()
   659  	inheritableTls.Set("World")
   660  	assert.Equal(t, "World", inheritableTls.Get())
   661  	//
   662  	assert.NotNil(t, createInheritedMap())
   663  	//
   664  	run := false
   665  	wg := &sync.WaitGroup{}
   666  	wg.Add(1)
   667  	Go(func() {
   668  		assert.NotNil(t, createInheritedMap())
   669  		//
   670  		assert.Equal(t, "", tls.Get())
   671  		assert.Equal(t, "World", inheritableTls.Get())
   672  		//
   673  		tls.Set("Hello2")
   674  		assert.Equal(t, "Hello2", tls.Get())
   675  		//
   676  		inheritableTls.Remove()
   677  		assert.Equal(t, "", inheritableTls.Get())
   678  		//
   679  		run = true
   680  		wg.Done()
   681  	})
   682  	wg.Wait()
   683  	assert.True(t, run)
   684  	//
   685  	assert.Equal(t, "Hello", tls.Get())
   686  	assert.Equal(t, "World", inheritableTls.Get())
   687  }
   688  
   689  func TestGo_Cross(t *testing.T) {
   690  	tls := NewThreadLocal[string]()
   691  	tls.Set("Hello")
   692  	assert.Equal(t, "Hello", tls.Get())
   693  	//
   694  	wg := &sync.WaitGroup{}
   695  	wg.Add(1)
   696  	Go(func() {
   697  		assert.Equal(t, "", tls.Get())
   698  		wg.Done()
   699  	})
   700  	wg.Wait()
   701  }
   702  
   703  func TestGoWait_Error(t *testing.T) {
   704  	run := false
   705  	task := GoWait(func(token CancelToken) {
   706  		run = true
   707  		panic("error")
   708  	})
   709  	assert.Panics(t, func() {
   710  		task.Get()
   711  	})
   712  	assert.True(t, run)
   713  	//
   714  	defer func() {
   715  		cause := recover()
   716  		assert.NotNil(t, cause)
   717  		assert.Implements(t, (*RuntimeError)(nil), cause)
   718  		err := cause.(RuntimeError)
   719  		lines := strings.Split(err.Error(), newLine)
   720  		assert.Equal(t, 6, len(lines))
   721  		//
   722  		line := lines[0]
   723  		assert.Equal(t, "RuntimeError: error", line)
   724  		//
   725  		line = lines[1]
   726  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.TestGoWait_Error."))
   727  		assert.True(t, strings.HasSuffix(line, "api_routine_test.go:707"))
   728  		//
   729  		line = lines[2]
   730  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.inheritedWaitTask.run()"))
   731  		assert.True(t, strings.HasSuffix(line, "routine.go:70"))
   732  		//
   733  		line = lines[3]
   734  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.(*futureTask[...]).Run()"))
   735  		assert.True(t, strings.HasSuffix(line, "future_task.go:108"))
   736  		//
   737  		line = lines[4]
   738  		assert.Equal(t, "   --- End of error stack trace ---", line)
   739  		//
   740  		line = lines[5]
   741  		assert.True(t, strings.HasPrefix(line, "   created by github.com/timandy/routine.GoWait()"))
   742  		assert.True(t, strings.HasSuffix(line, "api_routine.go:57"))
   743  	}()
   744  	task.Get()
   745  }
   746  
   747  func TestGoWait_Nil(t *testing.T) {
   748  	assert.Nil(t, createInheritedMap())
   749  	//
   750  	run := false
   751  	task := GoWait(func(token CancelToken) {
   752  		assert.Nil(t, createInheritedMap())
   753  		run = true
   754  	})
   755  	assert.Nil(t, task.Get())
   756  	assert.True(t, run)
   757  }
   758  
   759  func TestGoWait_Value(t *testing.T) {
   760  	tls := NewThreadLocal[string]()
   761  	tls.Set("Hello")
   762  	assert.Equal(t, "Hello", tls.Get())
   763  	//
   764  	inheritableTls := NewInheritableThreadLocal[string]()
   765  	inheritableTls.Set("World")
   766  	assert.Equal(t, "World", inheritableTls.Get())
   767  	//
   768  	assert.NotNil(t, createInheritedMap())
   769  	//
   770  	run := false
   771  	task := GoWait(func(token CancelToken) {
   772  		assert.NotNil(t, createInheritedMap())
   773  		//
   774  		assert.Equal(t, "", tls.Get())
   775  		assert.Equal(t, "World", inheritableTls.Get())
   776  		//
   777  		tls.Set("Hello2")
   778  		assert.Equal(t, "Hello2", tls.Get())
   779  		//
   780  		inheritableTls.Remove()
   781  		assert.Equal(t, "", inheritableTls.Get())
   782  		//
   783  		run = true
   784  	})
   785  	assert.Nil(t, task.Get())
   786  	assert.True(t, run)
   787  	//
   788  	assert.Equal(t, "Hello", tls.Get())
   789  	assert.Equal(t, "World", inheritableTls.Get())
   790  }
   791  
   792  func TestGoWait_Cross(t *testing.T) {
   793  	tls := NewThreadLocal[string]()
   794  	tls.Set("Hello")
   795  	assert.Equal(t, "Hello", tls.Get())
   796  	//
   797  	GoWait(func(token CancelToken) {
   798  		assert.Equal(t, "", tls.Get())
   799  	}).Get()
   800  }
   801  
   802  func TestGoWaitResult_Error(t *testing.T) {
   803  	run := false
   804  	task := GoWaitResult(func(token CancelToken) int {
   805  		run = true
   806  		if run {
   807  			panic("error")
   808  		}
   809  		return 1
   810  	})
   811  	assert.Panics(t, func() {
   812  		task.Get()
   813  	})
   814  	assert.True(t, run)
   815  	//
   816  	defer func() {
   817  		cause := recover()
   818  		assert.NotNil(t, cause)
   819  		assert.Implements(t, (*RuntimeError)(nil), cause)
   820  		err := cause.(RuntimeError)
   821  		lines := strings.Split(err.Error(), newLine)
   822  		assert.True(t, len(lines) == 6 || len(lines) == 7)
   823  		//
   824  		line := lines[0]
   825  		assert.Equal(t, "RuntimeError: error", line)
   826  		//
   827  		line = lines[1]
   828  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.TestGoWaitResult_Error."))
   829  		assert.True(t, strings.HasSuffix(line, "api_routine_test.go:807"))
   830  		//
   831  		line = lines[2]
   832  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.inheritedWaitResultTask[...].run()"))
   833  		assert.True(t, strings.HasSuffix(line, "routine.go:109"))
   834  		//
   835  		lineOffset := 0
   836  		if len(lines) == 7 {
   837  			line = lines[3+lineOffset]
   838  			lineOffset = 1
   839  			assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.WrapWaitResultTask[...].func1()"))
   840  			assert.True(t, strings.HasSuffix(line, "api_routine.go:41"))
   841  		}
   842  		//
   843  		line = lines[3+lineOffset]
   844  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.(*futureTask[...]).Run()"))
   845  		assert.True(t, strings.HasSuffix(line, "future_task.go:108"))
   846  		//
   847  		line = lines[4+lineOffset]
   848  		assert.Equal(t, "   --- End of error stack trace ---", line)
   849  		//
   850  		line = lines[5+lineOffset]
   851  		assert.True(t, strings.HasPrefix(line, "   created by github.com/timandy/routine.GoWaitResult[...]()"))
   852  		assert.True(t, strings.HasSuffix(line, "api_routine.go:66"))
   853  	}()
   854  	task.Get()
   855  }
   856  
   857  func TestGoWaitResult_Nil(t *testing.T) {
   858  	assert.Nil(t, createInheritedMap())
   859  	//
   860  	run := false
   861  	task := GoWaitResult(func(token CancelToken) bool {
   862  		assert.Nil(t, createInheritedMap())
   863  		run = true
   864  		return true
   865  	})
   866  	assert.True(t, task.Get())
   867  	assert.True(t, run)
   868  }
   869  
   870  func TestGoWaitResult_Value(t *testing.T) {
   871  	tls := NewThreadLocal[string]()
   872  	tls.Set("Hello")
   873  	assert.Equal(t, "Hello", tls.Get())
   874  	//
   875  	inheritableTls := NewInheritableThreadLocal[string]()
   876  	inheritableTls.Set("World")
   877  	assert.Equal(t, "World", inheritableTls.Get())
   878  	//
   879  	assert.NotNil(t, createInheritedMap())
   880  	//
   881  	run := false
   882  	task := GoWaitResult(func(token CancelToken) bool {
   883  		assert.NotNil(t, createInheritedMap())
   884  		//
   885  		assert.Equal(t, "", tls.Get())
   886  		assert.Equal(t, "World", inheritableTls.Get())
   887  		//
   888  		tls.Set("Hello2")
   889  		assert.Equal(t, "Hello2", tls.Get())
   890  		//
   891  		inheritableTls.Remove()
   892  		assert.Equal(t, "", inheritableTls.Get())
   893  		//
   894  		run = true
   895  		return true
   896  	})
   897  	assert.True(t, task.Get())
   898  	assert.True(t, run)
   899  	//
   900  	assert.Equal(t, "Hello", tls.Get())
   901  	assert.Equal(t, "World", inheritableTls.Get())
   902  }
   903  
   904  func TestGoWaitResult_Cross(t *testing.T) {
   905  	tls := NewThreadLocal[string]()
   906  	tls.Set("Hello")
   907  	assert.Equal(t, "Hello", tls.Get())
   908  	//
   909  	result := GoWaitResult(func(token CancelToken) string {
   910  		assert.Equal(t, "", tls.Get())
   911  		return tls.Get()
   912  	}).Get()
   913  	assert.Equal(t, "", result)
   914  }
   915  
   916  func captureStdout() (newStdout, oldStdout *os.File) {
   917  	oldStdout = os.Stdout
   918  	fileName := path.Join(os.TempDir(), "go_test_"+strconv.FormatInt(time.Now().UnixNano(), 10)+".txt")
   919  	file, err := os.Create(fileName)
   920  	if err != nil {
   921  		panic(err)
   922  	}
   923  	os.Stdout = file
   924  	newStdout = file
   925  	return
   926  }
   927  
   928  func restoreStdout(newStdout, oldStdout *os.File) {
   929  	os.Stdout = oldStdout
   930  	if err := newStdout.Close(); err != nil {
   931  		panic(err)
   932  	}
   933  	if err := os.Remove(newStdout.Name()); err != nil {
   934  		panic(err)
   935  	}
   936  }
   937  
   938  func readAll(rs io.ReadSeeker) string {
   939  	if _, err := rs.Seek(0, io.SeekStart); err != nil {
   940  		panic(err)
   941  	}
   942  	b := make([]byte, 0, 512)
   943  	for {
   944  		if len(b) == cap(b) {
   945  			b = append(b, 0)[:len(b)]
   946  		}
   947  		n, err := rs.Read(b[len(b):cap(b)])
   948  		b = b[:len(b)+n]
   949  		if err != nil {
   950  			if err == io.EOF {
   951  				return string(b)
   952  			}
   953  			panic(err)
   954  		}
   955  	}
   956  }