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

     1  package routine
     2  
     3  import (
     4  	"strings"
     5  	"sync"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  func TestFutureTask_IsDone(t *testing.T) {
    14  	task := NewFutureTask[*int](func(task FutureTask[*int]) *int { return nil })
    15  	assert.False(t, task.IsDone())
    16  	task.Complete(nil)
    17  	assert.True(t, task.IsDone())
    18  	//
    19  	task2 := NewFutureTask[*int](func(task FutureTask[*int]) *int { return nil })
    20  	assert.False(t, task2.IsDone())
    21  	task2.Cancel()
    22  	assert.True(t, task2.IsDone())
    23  	//
    24  	task3 := NewFutureTask[*int](func(task FutureTask[*int]) *int { return nil })
    25  	assert.False(t, task3.IsDone())
    26  	task3.Fail(nil)
    27  	assert.True(t, task3.IsDone())
    28  	//
    29  	task4 := NewFutureTask[*int](func(task FutureTask[*int]) *int { return nil })
    30  	assert.False(t, task4.IsDone())
    31  	task4.(*futureTask[*int]).state = taskStateRunning
    32  	assert.False(t, task4.IsDone())
    33  }
    34  
    35  func TestFutureTask_IsCanceled(t *testing.T) {
    36  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
    37  	assert.False(t, task.IsCanceled())
    38  	task.Cancel()
    39  	assert.True(t, task.IsCanceled())
    40  }
    41  
    42  func TestFutureTask_IsFailed(t *testing.T) {
    43  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
    44  	assert.False(t, task.IsFailed())
    45  	task.Fail(nil)
    46  	assert.True(t, task.IsFailed())
    47  }
    48  
    49  func TestFutureTask_Complete_AfterCancel(t *testing.T) {
    50  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
    51  	go func() {
    52  		task.Cancel()
    53  	}()
    54  	assert.Panics(t, func() {
    55  		task.Get()
    56  	})
    57  	assert.True(t, task.IsCanceled())
    58  	//
    59  	task.Complete(2)
    60  	assert.Panics(t, func() {
    61  		task.Get()
    62  	})
    63  	assert.True(t, task.IsCanceled())
    64  }
    65  
    66  func TestFutureTask_Complete_AfterComplete(t *testing.T) {
    67  	task := NewFutureTask(func(task FutureTask[int]) int { return 1 })
    68  	task.Run()
    69  	assert.Equal(t, 1, task.Get())
    70  	task.Complete(2)
    71  	assert.Equal(t, 1, task.Get())
    72  	//
    73  	run := false
    74  	task2 := NewFutureTask(func(task FutureTask[int]) int {
    75  		run = true
    76  		return 1
    77  	})
    78  	task2.Complete(2)
    79  	task2.Run()
    80  	assert.Equal(t, 2, task2.Get())
    81  	assert.False(t, run)
    82  }
    83  
    84  func TestFutureTask_Complete_Common(t *testing.T) {
    85  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
    86  	go func() {
    87  		task.Complete(1)
    88  	}()
    89  	assert.Equal(t, 1, task.Get())
    90  	//complete again won't change the result
    91  	go func() {
    92  		task.Complete(2)
    93  	}()
    94  	assert.Equal(t, 1, task.Get())
    95  }
    96  
    97  func TestFutureTask_Cancel_AfterComplete(t *testing.T) {
    98  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
    99  	go func() {
   100  		task.Complete(1)
   101  	}()
   102  	assert.Equal(t, 1, task.Get())
   103  	task.Cancel()
   104  	assert.False(t, task.IsCanceled())
   105  	assert.Equal(t, 1, task.Get())
   106  }
   107  
   108  func TestFutureTask_Cancel_Common(t *testing.T) {
   109  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
   110  	go func() {
   111  		task.Cancel()
   112  	}()
   113  	assert.Panics(t, func() {
   114  		task.Get()
   115  	})
   116  	assert.True(t, task.IsCanceled())
   117  	assert.Equal(t, "Task was canceled.", task.(*futureTask[int]).error.Message())
   118  	assert.Nil(t, task.(*futureTask[int]).error.Cause())
   119  }
   120  
   121  func TestFutureTask_Cancel_RuntimeError(t *testing.T) {
   122  	task3 := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
   123  	go func() {
   124  		task3.Cancel()
   125  	}()
   126  	assert.Panics(t, func() {
   127  		task3.Get()
   128  	})
   129  	assert.True(t, task3.IsCanceled())
   130  	assert.Equal(t, "Task was canceled.", task3.(*futureTask[int]).error.Message())
   131  	assert.Nil(t, task3.(*futureTask[int]).error.Cause())
   132  }
   133  
   134  func TestFutureTask_Fail_AfterComplete(t *testing.T) {
   135  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
   136  	go func() {
   137  		task.Complete(1)
   138  	}()
   139  	assert.Equal(t, 1, task.Get())
   140  	task.Fail(1)
   141  	assert.False(t, task.IsFailed())
   142  	assert.Equal(t, 1, task.Get())
   143  }
   144  
   145  func TestFutureTask_Fail_Common(t *testing.T) {
   146  	defer func() {
   147  		if cause := recover(); cause != nil {
   148  			err := cause.(RuntimeError)
   149  			assert.NotNil(t, err)
   150  			assert.Equal(t, "1", err.Message())
   151  			lines := strings.Split(err.Error(), newLine)
   152  			//
   153  			line := lines[0]
   154  			assert.Equal(t, "RuntimeError: 1", line)
   155  			//
   156  			line = lines[1]
   157  			assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.TestFutureTask_Fail_Common."))
   158  			assert.True(t, strings.HasSuffix(line, "future_task_test.go:169"))
   159  		}
   160  	}()
   161  	//
   162  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
   163  	go func() {
   164  		defer func() {
   165  			if cause := recover(); cause != nil {
   166  				task.Fail(cause)
   167  			}
   168  		}()
   169  		panic(1)
   170  	}()
   171  	task.Get()
   172  	assert.Fail(t, "should not be here")
   173  }
   174  
   175  func TestFutureTask_Fail_RuntimeError(t *testing.T) {
   176  	defer func() {
   177  		if cause := recover(); cause != nil {
   178  			err := cause.(RuntimeError)
   179  			assert.NotNil(t, err)
   180  			assert.Equal(t, "1", err.Message())
   181  			lines := strings.Split(err.Error(), newLine)
   182  			assert.Equal(t, 4, len(lines))
   183  			//
   184  			line := lines[0]
   185  			assert.Equal(t, "RuntimeError: 1", line)
   186  			//
   187  			line = lines[1]
   188  			assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.TestFutureTask_Fail_RuntimeError."))
   189  			assert.True(t, strings.HasSuffix(line, "future_task_test.go:207"))
   190  			//
   191  			line = lines[2]
   192  			assert.Equal(t, "   --- End of error stack trace ---", line)
   193  			//
   194  			line = lines[3]
   195  			assert.True(t, strings.HasPrefix(line, "   created by github.com/timandy/routine.TestFutureTask_Fail_RuntimeError()"))
   196  			assert.True(t, strings.HasSuffix(line, "future_task_test.go:201"))
   197  		}
   198  	}()
   199  	//
   200  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
   201  	go func() {
   202  		defer func() {
   203  			if cause := recover(); cause != nil {
   204  				task.Fail(NewRuntimeError(cause))
   205  			}
   206  		}()
   207  		panic(1)
   208  	}()
   209  	task.Get()
   210  	assert.Fail(t, "should not be here")
   211  }
   212  
   213  func TestFutureTask_Get_Nil(t *testing.T) {
   214  	run := false
   215  	task := NewFutureTask[*int](func(task FutureTask[*int]) *int { return nil })
   216  	go func() {
   217  		time.Sleep(100 * time.Millisecond)
   218  		run = true
   219  		task.Complete(nil)
   220  	}()
   221  	assert.Nil(t, task.Get())
   222  	assert.True(t, run)
   223  }
   224  
   225  func TestFutureTask_Get_Common(t *testing.T) {
   226  	run := false
   227  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
   228  	go func() {
   229  		time.Sleep(100 * time.Millisecond)
   230  		run = true
   231  		task.Complete(1)
   232  	}()
   233  	assert.Equal(t, 1, task.Get())
   234  	assert.True(t, run)
   235  }
   236  
   237  func TestFutureTask_GetWithTimeout_Complete(t *testing.T) {
   238  	wg := &sync.WaitGroup{}
   239  	wg.Add(1)
   240  	//
   241  	run := false
   242  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
   243  	go func() {
   244  		defer wg.Done()
   245  		//
   246  		if task.IsCanceled() {
   247  			return
   248  		}
   249  		run = true
   250  		task.Complete(1)
   251  	}()
   252  	assert.Equal(t, 1, task.GetWithTimeout(100*time.Millisecond))
   253  	assert.True(t, run)
   254  	//
   255  	wg.Wait()
   256  }
   257  
   258  func TestFutureTask_GetWithTimeout_Fail(t *testing.T) {
   259  	wg := &sync.WaitGroup{}
   260  	wg.Add(1)
   261  	//
   262  	run := false
   263  	task := NewFutureTask[int](func(task FutureTask[int]) int { return 0 })
   264  	go func() {
   265  		defer wg.Done()
   266  		//
   267  		if task.IsCanceled() {
   268  			return
   269  		}
   270  		run = true
   271  		task.Fail(1)
   272  	}()
   273  	assert.Panics(t, func() {
   274  		task.GetWithTimeout(100 * time.Millisecond)
   275  	})
   276  	assert.True(t, run)
   277  	//
   278  	assert.True(t, task.IsFailed())
   279  	assert.Equal(t, "1", task.(*futureTask[int]).error.Message())
   280  	assert.Nil(t, task.(*futureTask[int]).error.Cause())
   281  	//
   282  	wg.Wait()
   283  }
   284  
   285  func TestFutureTask_GetWithTimeout_Timeout(t *testing.T) {
   286  	wg := &sync.WaitGroup{}
   287  	wg.Add(1)
   288  	//
   289  	run := false
   290  	task := NewFutureTask[*int](func(task FutureTask[*int]) *int { return nil })
   291  	go func() {
   292  		defer wg.Done()
   293  		//
   294  		time.Sleep(100 * time.Millisecond)
   295  		if task.IsCanceled() {
   296  			return
   297  		}
   298  		run = true
   299  		task.Complete(nil)
   300  	}()
   301  	assert.Panics(t, func() {
   302  		task.GetWithTimeout(1 * time.Millisecond)
   303  	})
   304  	assert.False(t, run)
   305  	//
   306  	assert.True(t, task.IsCanceled())
   307  	assert.Equal(t, "Task execution timeout after 1ms.", task.(*futureTask[*int]).error.Message())
   308  	assert.Nil(t, task.(*futureTask[*int]).error.Cause())
   309  	//
   310  	wg.Wait()
   311  }
   312  
   313  func TestFutureTask_Run_AfterCancel(t *testing.T) {
   314  	run := false
   315  	task := NewFutureTask(func(task FutureTask[*int]) *int {
   316  		run = true
   317  		return nil
   318  	})
   319  	task.Cancel()
   320  	task.Run()
   321  	assert.Panics(t, func() {
   322  		task.Get()
   323  	})
   324  	assert.True(t, task.IsCanceled())
   325  	assert.False(t, run)
   326  }
   327  
   328  func TestFutureTask_Run_AfterFail(t *testing.T) {
   329  	run := false
   330  	task := NewFutureTask(func(task FutureTask[*int]) *int {
   331  		run = true
   332  		return nil
   333  	})
   334  	task.Fail("failed.")
   335  	task.Run()
   336  	assert.Panics(t, func() {
   337  		task.Get()
   338  	})
   339  	assert.True(t, task.IsFailed())
   340  	assert.False(t, run)
   341  }
   342  
   343  func TestFutureTask_Run_AfterComplete(t *testing.T) {
   344  	run := false
   345  	task := NewFutureTask(func(task FutureTask[int]) int {
   346  		run = true
   347  		return 0
   348  	})
   349  	task.Complete(1)
   350  	task.Run()
   351  	assert.Equal(t, 1, task.Get())
   352  	assert.True(t, task.IsDone())
   353  	assert.False(t, run)
   354  }
   355  
   356  func TestFutureTask_Run_AfterRun(t *testing.T) {
   357  	var run int32 = 0
   358  	wg := &sync.WaitGroup{}
   359  	wg.Add(1)
   360  	wg2 := &sync.WaitGroup{}
   361  	wg2.Add(1)
   362  	task := NewFutureTask(func(task FutureTask[int]) int {
   363  		atomic.AddInt32(&run, 1)
   364  		wg.Done()
   365  		wg2.Wait()
   366  		return 1
   367  	})
   368  	go task.Run()
   369  	wg.Wait()
   370  	task.Run()
   371  	wg2.Done()
   372  	assert.Equal(t, 1, task.Get())
   373  	assert.True(t, task.IsDone())
   374  	assert.Equal(t, int32(1), atomic.LoadInt32(&run))
   375  }
   376  
   377  func TestFutureTask_Run_Normal(t *testing.T) {
   378  	run := false
   379  	wg := &sync.WaitGroup{}
   380  	wg.Add(1)
   381  	task := NewFutureTask(func(task FutureTask[int]) int {
   382  		run = true
   383  		return 1
   384  	})
   385  	go task.Run()
   386  	assert.Equal(t, 1, task.Get())
   387  	assert.True(t, task.IsDone())
   388  	assert.True(t, run)
   389  }
   390  
   391  func TestFutureTask_Run_Error(t *testing.T) {
   392  	run := false
   393  	wg := &sync.WaitGroup{}
   394  	wg.Add(1)
   395  	task := NewFutureTask(func(task FutureTask[int]) int {
   396  		run = true
   397  		panic(1)
   398  	})
   399  	go task.Run()
   400  	assert.Panics(t, func() {
   401  		task.Get()
   402  	})
   403  	assert.True(t, task.IsFailed())
   404  	assert.True(t, run)
   405  	//
   406  	defer func() {
   407  		cause := recover()
   408  		assert.NotNil(t, cause)
   409  		assert.Implements(t, (*RuntimeError)(nil), cause)
   410  		err := cause.(RuntimeError)
   411  		assert.Equal(t, "1", err.Message())
   412  		lines := strings.Split(err.Error(), newLine)
   413  		assert.Equal(t, 5, len(lines))
   414  		//
   415  		line := lines[0]
   416  		assert.Equal(t, "RuntimeError: 1", line)
   417  		//
   418  		line = lines[1]
   419  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.TestFutureTask_Run_Error."))
   420  		assert.True(t, strings.HasSuffix(line, "future_task_test.go:397"))
   421  		//
   422  		line = lines[2]
   423  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.(*futureTask[...]).Run()"))
   424  		assert.True(t, strings.HasSuffix(line, "future_task.go:108"))
   425  		//
   426  		line = lines[3]
   427  		assert.Equal(t, "   --- End of error stack trace ---", line)
   428  		//
   429  		line = lines[4]
   430  		assert.True(t, strings.HasPrefix(line, "   created by github.com/timandy/routine.TestFutureTask_Run_Error()"))
   431  		assert.True(t, strings.HasSuffix(line, "future_task_test.go:399"))
   432  	}()
   433  	task.Get()
   434  	assert.Fail(t, "should not be here")
   435  }
   436  
   437  func TestFutureTask_Run_RuntimeError(t *testing.T) {
   438  	run := false
   439  	wg := &sync.WaitGroup{}
   440  	wg.Add(1)
   441  	task := NewFutureTask(func(task FutureTask[int]) int {
   442  		run = true
   443  		err := NewRuntimeError(1)
   444  		panic(err)
   445  	})
   446  	go task.Run()
   447  	assert.Panics(t, func() {
   448  		task.Get()
   449  	})
   450  	assert.True(t, task.IsFailed())
   451  	assert.True(t, run)
   452  	//
   453  	defer func() {
   454  		cause := recover()
   455  		assert.NotNil(t, cause)
   456  		assert.Implements(t, (*RuntimeError)(nil), cause)
   457  		err := cause.(RuntimeError)
   458  		assert.Equal(t, "1", err.Message())
   459  		lines := strings.Split(err.Error(), newLine)
   460  		assert.Equal(t, 5, len(lines))
   461  		//
   462  		line := lines[0]
   463  		assert.Equal(t, "RuntimeError: 1", line)
   464  		//
   465  		line = lines[1]
   466  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.TestFutureTask_Run_RuntimeError."))
   467  		assert.True(t, strings.HasSuffix(line, "future_task_test.go:443"))
   468  		//
   469  		line = lines[2]
   470  		assert.True(t, strings.HasPrefix(line, "   at github.com/timandy/routine.(*futureTask[...]).Run()"))
   471  		assert.True(t, strings.HasSuffix(line, "future_task.go:108"))
   472  		//
   473  		line = lines[3]
   474  		assert.Equal(t, "   --- End of error stack trace ---", line)
   475  		//
   476  		line = lines[4]
   477  		assert.True(t, strings.HasPrefix(line, "   created by github.com/timandy/routine.TestFutureTask_Run_RuntimeError()"))
   478  		assert.True(t, strings.HasSuffix(line, "future_task_test.go:446"))
   479  	}()
   480  	task.Get()
   481  	assert.Fail(t, "should not be here")
   482  }
   483  
   484  func TestFutureTask_Routine_Complete(t *testing.T) {
   485  	wg := &sync.WaitGroup{}
   486  	wg.Add(1)
   487  	//
   488  	task := GoWaitResult(func(token CancelToken) int {
   489  		defer wg.Done()
   490  		//
   491  		if token.IsCanceled() {
   492  			panic("canceled")
   493  		}
   494  		time.Sleep(1 * time.Millisecond)
   495  		return 1
   496  	})
   497  	assert.Equal(t, 1, task.GetWithTimeout(100*time.Millisecond))
   498  	//
   499  	wg.Wait()
   500  }
   501  
   502  func TestFutureTask_Routine_Cancel(t *testing.T) {
   503  	wg := &sync.WaitGroup{}
   504  	wg.Add(1)
   505  	//
   506  	task := GoWaitResult(func(token CancelToken) int {
   507  		defer wg.Done()
   508  		//
   509  		token.Cancel()
   510  		return 1
   511  	})
   512  	assert.Panics(t, func() {
   513  		task.GetWithTimeout(100 * time.Millisecond)
   514  	})
   515  	assert.True(t, task.IsCanceled())
   516  	assert.Equal(t, "Task was canceled.", task.(*futureTask[int]).error.Message())
   517  	assert.Nil(t, task.(*futureTask[int]).error.Cause())
   518  	//
   519  	wg.Wait()
   520  }
   521  
   522  func TestFutureTask_Routine_CancelInParent(t *testing.T) {
   523  	wg := &sync.WaitGroup{}
   524  	wg.Add(1)
   525  	wg2 := &sync.WaitGroup{}
   526  	wg2.Add(1)
   527  	//
   528  	finished := false
   529  	task := GoWaitResult(func(token CancelToken) int {
   530  		wg2.Done()
   531  		defer wg.Done()
   532  		//
   533  		for i := 0; i < 10; i++ {
   534  			time.Sleep(10 * time.Millisecond)
   535  			if token.IsCanceled() {
   536  				return 0
   537  			}
   538  		}
   539  		finished = true
   540  		return 1
   541  	})
   542  	wg2.Wait()
   543  	task.Cancel()
   544  	//
   545  	wg.Wait()
   546  	//
   547  	assert.False(t, finished)
   548  	assert.True(t, task.IsCanceled())
   549  	assert.Equal(t, "Task was canceled.", task.(*futureTask[int]).error.Message())
   550  	assert.Nil(t, task.(*futureTask[int]).error.Cause())
   551  }
   552  
   553  func TestFutureTask_Routine_Fail(t *testing.T) {
   554  	wg := &sync.WaitGroup{}
   555  	wg.Add(1)
   556  	//
   557  	task := GoWaitResult(func(token CancelToken) int {
   558  		defer wg.Done()
   559  		//
   560  		if token.IsCanceled() {
   561  			return 1
   562  		}
   563  		panic("something error")
   564  	})
   565  	assert.Panics(t, func() {
   566  		task.GetWithTimeout(100 * time.Millisecond)
   567  	})
   568  	assert.True(t, task.IsFailed())
   569  	assert.Equal(t, "something error", task.(*futureTask[int]).error.Message())
   570  	assert.Nil(t, task.(*futureTask[int]).error.Cause())
   571  	//
   572  	wg.Wait()
   573  }
   574  
   575  func TestFutureTask_Routine_Timeout(t *testing.T) {
   576  	wg := &sync.WaitGroup{}
   577  	wg.Add(1)
   578  	//
   579  	task := GoWaitResult(func(token CancelToken) int {
   580  		defer wg.Done()
   581  		//
   582  		for i := 0; i < 10; i++ {
   583  			if token.IsCanceled() {
   584  				panic("canceled")
   585  			}
   586  			time.Sleep(10 * time.Millisecond)
   587  		}
   588  		return 1
   589  	})
   590  	assert.Panics(t, func() {
   591  		task.GetWithTimeout(1 * time.Millisecond)
   592  	})
   593  	assert.True(t, task.IsCanceled())
   594  	assert.Equal(t, "Task execution timeout after 1ms.", task.(*futureTask[int]).error.Message())
   595  	assert.Nil(t, task.(*futureTask[int]).error.Cause())
   596  	//
   597  	wg.Wait()
   598  }
   599  
   600  func TestFutureTask_Routine_TimeoutThenComplete(t *testing.T) {
   601  	wg := &sync.WaitGroup{}
   602  	wg.Add(1)
   603  	//
   604  	task := GoWait(func(token CancelToken) {
   605  		defer wg.Done()
   606  		//
   607  		ft := token.(*futureTask[any])
   608  		ft.result = 1
   609  		assert.True(t, atomic.CompareAndSwapInt32(&ft.state, taskStateRunning, taskStateCompleted))
   610  		time.Sleep(50 * time.Millisecond)
   611  		ft.await.Done()
   612  	})
   613  	assert.Equal(t, 1, task.GetWithTimeout(10*time.Millisecond))
   614  	assert.Equal(t, 1, task.Get())
   615  	//
   616  	wg.Wait()
   617  }
   618  
   619  func TestFutureTask_Routine_TimeoutThenCancel(t *testing.T) {
   620  	wg := &sync.WaitGroup{}
   621  	wg.Add(1)
   622  	//
   623  	task := GoWait(func(token CancelToken) {
   624  		defer wg.Done()
   625  		//
   626  		ft := token.(*futureTask[any])
   627  		ft.error = NewRuntimeError("canceled.")
   628  		assert.True(t, atomic.CompareAndSwapInt32(&ft.state, taskStateRunning, taskStateCanceled))
   629  		time.Sleep(50 * time.Millisecond)
   630  		ft.await.Done()
   631  	})
   632  	assert.Panics(t, func() {
   633  		task.GetWithTimeout(10 * time.Millisecond)
   634  	})
   635  	//
   636  	assert.True(t, task.IsCanceled())
   637  	assert.Equal(t, "canceled.", task.(*futureTask[any]).error.Message())
   638  	assert.Nil(t, task.(*futureTask[any]).error.Cause())
   639  	assert.Panics(t, func() {
   640  		task.Get()
   641  	})
   642  	//
   643  	wg.Wait()
   644  }
   645  
   646  func TestFutureTask_Routine_TimeoutThenFail(t *testing.T) {
   647  	wg := &sync.WaitGroup{}
   648  	wg.Add(1)
   649  	//
   650  	task := GoWait(func(token CancelToken) {
   651  		defer wg.Done()
   652  		//
   653  		ft := token.(*futureTask[any])
   654  		ft.error = NewRuntimeError("failed.")
   655  		assert.True(t, atomic.CompareAndSwapInt32(&ft.state, taskStateRunning, taskStateFailed))
   656  		time.Sleep(50 * time.Millisecond)
   657  		ft.await.Done()
   658  	})
   659  	assert.Panics(t, func() {
   660  		task.GetWithTimeout(10 * time.Millisecond)
   661  	})
   662  	//
   663  	assert.True(t, task.IsFailed())
   664  	assert.Equal(t, "failed.", task.(*futureTask[any]).error.Message())
   665  	assert.Nil(t, task.(*futureTask[any]).error.Cause())
   666  	assert.Panics(t, func() {
   667  		task.Get()
   668  	})
   669  	//
   670  	wg.Wait()
   671  }