go.uber.org/cadence@v1.2.9/internal/internal_workflow_test.go (about)

     1  // Copyright (c) 2017 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 internal
    22  
    23  import (
    24  	"context"
    25  	"errors"
    26  	"fmt"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/stretchr/testify/mock"
    32  	"github.com/stretchr/testify/require"
    33  	"github.com/stretchr/testify/suite"
    34  	"go.uber.org/zap/zaptest"
    35  
    36  	"go.uber.org/cadence/internal/common/metrics"
    37  )
    38  
    39  type WorkflowUnitTest struct {
    40  	suite.Suite
    41  	WorkflowTestSuite
    42  	activityOptions ActivityOptions
    43  }
    44  
    45  func (s *WorkflowUnitTest) SetupSuite() {
    46  	s.activityOptions = ActivityOptions{
    47  		ScheduleToStartTimeout: time.Minute,
    48  		StartToCloseTimeout:    time.Minute,
    49  		HeartbeatTimeout:       20 * time.Second,
    50  	}
    51  }
    52  func (s *WorkflowUnitTest) SetupTest() {
    53  	s.SetLogger(zaptest.NewLogger(s.T()))
    54  }
    55  
    56  func TestWorkflowUnitTest(t *testing.T) {
    57  	suite.Run(t, new(WorkflowUnitTest))
    58  }
    59  
    60  func worldWorkflow(ctx Context, input string) (result string, err error) {
    61  	return input + " World!", nil
    62  }
    63  
    64  func (s *WorkflowUnitTest) Test_WorldWorkflow() {
    65  	env := newTestWorkflowEnv(s.T())
    66  	env.ExecuteWorkflow(worldWorkflow, "Hello")
    67  	s.True(env.IsWorkflowCompleted())
    68  	s.NoError(env.GetWorkflowError())
    69  }
    70  
    71  func helloWorldAct(ctx context.Context) (string, error) {
    72  	s := ctx.Value(unitTestKey).(*WorkflowUnitTest)
    73  	info := GetActivityInfo(ctx)
    74  	s.Equal(tasklist, info.TaskList)
    75  	s.Equal(2*time.Second, info.HeartbeatTimeout)
    76  	return "test", nil
    77  }
    78  
    79  type key int
    80  
    81  const unitTestKey key = 1
    82  
    83  func singleActivityWorkflowWithOptions(s *WorkflowUnitTest, ao ActivityOptions) error {
    84  	helloWorldActivityWorkflow := func(ctx Context, input string) (result string, err error) {
    85  		ctx1 := WithActivityOptions(ctx, ao)
    86  		f := ExecuteActivity(ctx1, helloWorldAct)
    87  		var r1 string
    88  		err = f.Get(ctx, &r1)
    89  		if err != nil {
    90  			return "", err
    91  		}
    92  		return r1, nil
    93  	}
    94  
    95  	env := newTestWorkflowEnv(s.T())
    96  	ctx := context.WithValue(context.Background(), unitTestKey, s)
    97  	env.SetWorkerOptions(WorkerOptions{BackgroundActivityContext: ctx})
    98  	env.RegisterActivity(helloWorldAct)
    99  	env.ExecuteWorkflow(helloWorldActivityWorkflow, "Hello")
   100  	s.True(env.IsWorkflowCompleted())
   101  	return env.GetWorkflowError()
   102  }
   103  
   104  func (s *WorkflowUnitTest) Test_SingleActivityWorkflow() {
   105  	ao := ActivityOptions{
   106  		ScheduleToStartTimeout: 10 * time.Second,
   107  		StartToCloseTimeout:    5 * time.Second,
   108  		HeartbeatTimeout:       2 * time.Second,
   109  		ActivityID:             "id1",
   110  		TaskList:               tasklist,
   111  	}
   112  	err := singleActivityWorkflowWithOptions(s, ao)
   113  	s.NoError(err)
   114  }
   115  
   116  func (s *WorkflowUnitTest) Test_SingleActivityWorkflowIsErrorMessagesMatched() {
   117  	testCases := []struct {
   118  		name                   string
   119  		ScheduleToStartTimeout time.Duration
   120  		StartToCloseTimeout    time.Duration
   121  		ScheduleToCloseTimeout time.Duration
   122  		expectedErrorMessage   string
   123  	}{
   124  		{
   125  			name:                   "ZeroScheduleToStartTimeout",
   126  			ScheduleToStartTimeout: 0 * time.Second,
   127  			StartToCloseTimeout:    5 * time.Second,
   128  			ScheduleToCloseTimeout: 0 * time.Second,
   129  			expectedErrorMessage:   "missing or negative ScheduleToStartTimeoutSeconds",
   130  		},
   131  		{
   132  			name:                   "ZeroStartToCloseTimeout",
   133  			ScheduleToStartTimeout: 10 * time.Second,
   134  			StartToCloseTimeout:    0 * time.Second,
   135  			ScheduleToCloseTimeout: 0 * time.Second,
   136  			expectedErrorMessage:   "missing or negative StartToCloseTimeoutSeconds",
   137  		},
   138  		{
   139  			name:                   "NegativeScheduleToCloseTimeout",
   140  			ScheduleToStartTimeout: 10 * time.Second,
   141  			StartToCloseTimeout:    5 * time.Second,
   142  			ScheduleToCloseTimeout: -1 * time.Second,
   143  			expectedErrorMessage:   "invalid negative ScheduleToCloseTimeoutSeconds",
   144  		},
   145  	}
   146  
   147  	for _, testCase := range testCases {
   148  		ao := ActivityOptions{
   149  			ScheduleToStartTimeout: testCase.ScheduleToStartTimeout,
   150  			StartToCloseTimeout:    testCase.StartToCloseTimeout,
   151  			ScheduleToCloseTimeout: testCase.ScheduleToCloseTimeout,
   152  			HeartbeatTimeout:       2 * time.Second,
   153  			ActivityID:             "id1",
   154  			TaskList:               tasklist,
   155  		}
   156  		s.Run(testCase.name, func() {
   157  			err := singleActivityWorkflowWithOptions(s, ao)
   158  			s.ErrorContains(err, testCase.expectedErrorMessage)
   159  		})
   160  	}
   161  }
   162  
   163  func splitJoinActivityWorkflow(ctx Context, testPanic bool) (result string, err error) {
   164  	var result1, result2 string
   165  	var err1, err2 error
   166  
   167  	ao := ActivityOptions{
   168  		ScheduleToStartTimeout: 10 * time.Second,
   169  		StartToCloseTimeout:    5 * time.Second,
   170  	}
   171  	ctx = WithActivityOptions(ctx, ao)
   172  
   173  	c1 := NewChannel(ctx)
   174  	c2 := NewChannel(ctx)
   175  	Go(ctx, func(ctx Context) {
   176  		ao.ActivityID = "id1"
   177  		ctx1 := WithActivityOptions(ctx, ao)
   178  		f := ExecuteActivity(ctx1, testAct)
   179  		err1 = f.Get(ctx, &result1)
   180  		if err1 == nil {
   181  			c1.Send(ctx, true)
   182  		}
   183  	})
   184  	Go(ctx, func(ctx Context) {
   185  		ao.ActivityID = "id2"
   186  		ctx2 := WithActivityOptions(ctx, ao)
   187  		f := ExecuteActivity(ctx2, testAct)
   188  		err1 := f.Get(ctx, &result2)
   189  		if testPanic {
   190  			panic("simulated")
   191  		}
   192  		if err1 == nil {
   193  			c2.Send(ctx, true)
   194  		}
   195  	})
   196  
   197  	c1.Receive(ctx, nil)
   198  	// Use selector to test it
   199  	selected := false
   200  	NewSelector(ctx).AddReceive(c2, func(c Channel, more bool) {
   201  		if !more {
   202  			panic("more should be true")
   203  		}
   204  		selected = true
   205  	}).Select(ctx)
   206  	if !selected {
   207  		return "", errors.New("selector does not work")
   208  	}
   209  	if err1 != nil {
   210  		return "", err1
   211  	}
   212  	if err2 != nil {
   213  		return "", err2
   214  	}
   215  
   216  	return result1 + result2, nil
   217  }
   218  
   219  func returnPanicWorkflow(ctx Context) (err error) {
   220  	return newPanicError("panicError", "stackTrace")
   221  }
   222  
   223  func (s *WorkflowUnitTest) Test_SplitJoinActivityWorkflow() {
   224  	env := newTestWorkflowEnv(s.T())
   225  	env.RegisterWorkflowWithOptions(splitJoinActivityWorkflow, RegisterWorkflowOptions{Name: "splitJoinActivityWorkflow"})
   226  	env.RegisterActivityWithOptions(testAct, RegisterActivityOptions{Name: "testActivityWithOptions"})
   227  	env.OnActivity(testAct, mock.Anything).Return(func(ctx context.Context) (string, error) {
   228  		activityID := GetActivityInfo(ctx).ActivityID
   229  		switch activityID {
   230  		case "id1":
   231  			return "Hello", nil
   232  		case "id2":
   233  			return " Flow!", nil
   234  		default:
   235  			panic(fmt.Sprintf("Unexpected activityID: %v", activityID))
   236  		}
   237  	}).Twice()
   238  	tracer := tracingInterceptorFactory{}
   239  	env.SetWorkerOptions(WorkerOptions{WorkflowInterceptorChainFactories: []WorkflowInterceptorFactory{&tracer}})
   240  	env.ExecuteWorkflow(splitJoinActivityWorkflow, false)
   241  	s.True(env.IsWorkflowCompleted())
   242  	s.NoError(env.GetWorkflowError())
   243  	env.AssertExpectations(s.T())
   244  	var result string
   245  	env.GetWorkflowResult(&result)
   246  	s.Equal("Hello Flow!", result)
   247  	s.Equal(1, len(tracer.instances))
   248  	trace := tracer.instances[len(tracer.instances)-1].trace
   249  	s.Equal([]string{
   250  		"ExecuteWorkflow splitJoinActivityWorkflow begin",
   251  		"ExecuteActivity testActivityWithOptions",
   252  		"ExecuteActivity testActivityWithOptions",
   253  		"ExecuteWorkflow splitJoinActivityWorkflow end",
   254  	}, trace)
   255  }
   256  
   257  func TestWorkflowPanic(t *testing.T) {
   258  	env := newTestWorkflowEnv(t)
   259  	env.RegisterActivity(testAct)
   260  	env.ExecuteWorkflow(splitJoinActivityWorkflow, true)
   261  	require.True(t, env.IsWorkflowCompleted())
   262  	require.NotNil(t, env.GetWorkflowError())
   263  	resultErr := env.GetWorkflowError().(*PanicError)
   264  	require.EqualValues(t, "simulated", resultErr.Error())
   265  	require.Contains(t, resultErr.StackTrace(), "cadence/internal.splitJoinActivityWorkflow")
   266  }
   267  
   268  func TestWorkflowReturnsPanic(t *testing.T) {
   269  	env := newTestWorkflowEnv(t)
   270  	env.ExecuteWorkflow(returnPanicWorkflow)
   271  	require.True(t, env.IsWorkflowCompleted())
   272  	require.NotNil(t, env.GetWorkflowError())
   273  	resultErr := env.GetWorkflowError().(*PanicError)
   274  	require.EqualValues(t, "panicError", resultErr.Error())
   275  	require.EqualValues(t, "stackTrace", resultErr.StackTrace())
   276  }
   277  
   278  func testClockWorkflow(ctx Context) (time.Time, error) {
   279  	c := Now(ctx)
   280  	return c, nil
   281  }
   282  
   283  func (s *WorkflowUnitTest) Test_ClockWorkflow() {
   284  	env := newTestWorkflowEnv(s.T())
   285  	env.ExecuteWorkflow(testClockWorkflow)
   286  	s.True(env.IsWorkflowCompleted())
   287  	s.NoError(env.GetWorkflowError())
   288  	var nowTime time.Time
   289  	env.GetWorkflowResult(&nowTime)
   290  	s.False(nowTime.IsZero())
   291  }
   292  
   293  type testTimerWorkflow struct {
   294  	t *testing.T
   295  }
   296  
   297  func (w *testTimerWorkflow) Execute(ctx Context, input []byte) (result []byte, err error) {
   298  	// Start a timer.
   299  	t := NewTimer(ctx, 1)
   300  
   301  	isWokeByTimer := false
   302  
   303  	NewSelector(ctx).AddFuture(t, func(f Future) {
   304  		err := f.Get(ctx, nil)
   305  		require.NoError(w.t, err)
   306  		isWokeByTimer = true
   307  	}).Select(ctx)
   308  
   309  	require.True(w.t, isWokeByTimer)
   310  
   311  	// Start a timer and cancel it.
   312  	ctx2, c2 := WithCancel(ctx)
   313  	t2 := NewTimer(ctx2, 1)
   314  	c2()
   315  	err2 := t2.Get(ctx2, nil)
   316  
   317  	require.Error(w.t, err2)
   318  	_, isCancelErr := err2.(*CanceledError)
   319  	require.True(w.t, isCancelErr)
   320  
   321  	// Sleep 1 sec
   322  	ctx3, _ := WithCancel(ctx)
   323  	err3 := Sleep(ctx3, 1)
   324  	require.NoError(w.t, err3)
   325  
   326  	// Sleep and cancel.
   327  	ctx4, c4 := WithCancel(ctx)
   328  	c4()
   329  	err4 := Sleep(ctx4, 1)
   330  
   331  	require.Error(w.t, err4)
   332  	_, isCancelErr = err4.(*CanceledError)
   333  	require.True(w.t, isCancelErr)
   334  
   335  	return []byte("workflow-completed"), nil
   336  }
   337  
   338  func TestTimerWorkflow(t *testing.T) {
   339  	env := newTestWorkflowEnv(t)
   340  	w := &testTimerWorkflow{t: t}
   341  	env.RegisterWorkflow(w.Execute)
   342  	env.ExecuteWorkflow(w.Execute, []byte{1, 2})
   343  	require.True(t, env.IsWorkflowCompleted())
   344  	require.NoError(t, env.GetWorkflowError())
   345  }
   346  
   347  type testActivityCancelWorkflow struct {
   348  	t *testing.T
   349  }
   350  
   351  func testAct(ctx context.Context) (string, error) {
   352  	return "test", nil
   353  }
   354  
   355  func (w *testActivityCancelWorkflow) Execute(ctx Context, input []byte) (result []byte, err error) {
   356  	ao := ActivityOptions{
   357  		ScheduleToStartTimeout: 10 * time.Second,
   358  		StartToCloseTimeout:    5 * time.Second,
   359  	}
   360  	ctx = WithActivityOptions(ctx, ao)
   361  
   362  	// Sync cancellation
   363  	ctx1, c1 := WithCancel(ctx)
   364  	defer c1()
   365  
   366  	ao.ActivityID = "id1"
   367  	ctx1 = WithActivityOptions(ctx1, ao)
   368  	f := ExecuteActivity(ctx1, testAct)
   369  	var res1 string
   370  	err1 := f.Get(ctx, &res1)
   371  	require.NoError(w.t, err1, err1)
   372  	require.Equal(w.t, res1, "test")
   373  
   374  	// Async Cancellation (Callback completes before cancel)
   375  	ctx2, c2 := WithCancel(ctx)
   376  	ao.ActivityID = "id2"
   377  	ctx2 = WithActivityOptions(ctx2, ao)
   378  	f = ExecuteActivity(ctx2, testAct)
   379  	c2()
   380  	var res2 string
   381  	err2 := f.Get(ctx, &res2)
   382  	require.NotNil(w.t, err2)
   383  	_, ok := err2.(*CanceledError)
   384  	require.True(w.t, ok)
   385  	return []byte("workflow-completed"), nil
   386  }
   387  
   388  func TestActivityCancellation(t *testing.T) {
   389  	env := newTestWorkflowEnv(t)
   390  	env.RegisterActivity(testAct)
   391  	w := &testActivityCancelWorkflow{t: t}
   392  	env.RegisterWorkflow(w.Execute)
   393  	env.ExecuteWorkflow(w.Execute, []byte{1, 2})
   394  	require.True(t, env.IsWorkflowCompleted())
   395  	require.NoError(t, env.GetWorkflowError())
   396  }
   397  
   398  type sayGreetingActivityRequest struct {
   399  	Name     string
   400  	Greeting string
   401  }
   402  
   403  func getGreetingActivity() (string, error) {
   404  	return "Hello", nil
   405  }
   406  func getNameActivity() (string, error) {
   407  	return "cadence", nil
   408  }
   409  func sayGreetingActivity(input *sayGreetingActivityRequest) (string, error) {
   410  	return fmt.Sprintf("%v %v!", input.Greeting, input.Name), nil
   411  }
   412  
   413  // Greetings Workflow Decider.
   414  func greetingsWorkflow(ctx Context) (result string, err error) {
   415  	// Get Greeting.
   416  	ao := ActivityOptions{
   417  		ScheduleToStartTimeout: 10 * time.Second,
   418  		StartToCloseTimeout:    5 * time.Second,
   419  	}
   420  	ctx1 := WithActivityOptions(ctx, ao)
   421  
   422  	f := ExecuteActivity(ctx1, getGreetingActivity)
   423  	var greetResult string
   424  	err = f.Get(ctx, &greetResult)
   425  	if err != nil {
   426  		return "", err
   427  	}
   428  
   429  	// Get Name.
   430  	f = ExecuteActivity(ctx1, getNameActivity)
   431  	var nameResult string
   432  	err = f.Get(ctx, &nameResult)
   433  	if err != nil {
   434  		return "", err
   435  	}
   436  
   437  	// Say Greeting.
   438  	request := &sayGreetingActivityRequest{Name: nameResult, Greeting: greetResult}
   439  	err = ExecuteActivity(ctx1, sayGreetingActivity, request).Get(ctx, &result)
   440  	if err != nil {
   441  		return "", err
   442  	}
   443  	return result, nil
   444  }
   445  
   446  func (s *WorkflowUnitTest) Test_ExternalExampleWorkflow() {
   447  	env := newTestWorkflowEnv(s.T())
   448  	env.RegisterActivity(getGreetingActivity)
   449  	env.RegisterActivity(getNameActivity)
   450  	env.RegisterActivity(sayGreetingActivity)
   451  
   452  	env.ExecuteWorkflow(greetingsWorkflow)
   453  
   454  	s.True(env.IsWorkflowCompleted())
   455  	s.NoError(env.GetWorkflowError())
   456  	var result string
   457  	env.GetWorkflowResult(&result)
   458  	s.Equal("Hello cadence!", result)
   459  }
   460  
   461  func continueAsNewWorkflowTest(ctx Context) error {
   462  	return NewContinueAsNewError(ctx, "continueAsNewWorkflowTest", []byte("start"))
   463  }
   464  
   465  func (s *WorkflowUnitTest) Test_ContinueAsNewWorkflow() {
   466  	env := newTestWorkflowEnv(s.T())
   467  	env.ExecuteWorkflow(continueAsNewWorkflowTest)
   468  	s.True(env.IsWorkflowCompleted())
   469  	s.NotNil(env.GetWorkflowError())
   470  	resultErr := env.GetWorkflowError().(*ContinueAsNewError)
   471  	s.EqualValues("continueAsNewWorkflowTest", resultErr.params.workflowType.Name)
   472  	s.EqualValues(1, *resultErr.params.executionStartToCloseTimeoutSeconds)
   473  	s.EqualValues(1, *resultErr.params.taskStartToCloseTimeoutSeconds)
   474  	s.EqualValues("default-test-tasklist", *resultErr.params.taskListName)
   475  }
   476  
   477  func cancelWorkflowTest(ctx Context) (string, error) {
   478  	if ctx.Done().Receive(ctx, nil); ctx.Err() == ErrCanceled {
   479  		return "Cancelled.", ctx.Err()
   480  	}
   481  	return "Completed.", nil
   482  }
   483  
   484  func (s *WorkflowUnitTest) Test_CancelWorkflow() {
   485  	env := newTestWorkflowEnv(s.T())
   486  	env.RegisterDelayedCallback(func() {
   487  		env.CancelWorkflow()
   488  	}, time.Hour)
   489  	env.ExecuteWorkflow(cancelWorkflowTest)
   490  	s.True(env.IsWorkflowCompleted(), "Workflow failed to complete")
   491  }
   492  
   493  func cancelWorkflowAfterActivityTest(ctx Context) ([]byte, error) {
   494  	// The workflow cancellation should handle activity and timer cancellation
   495  	// not to propagate those decisions.
   496  
   497  	// schedule an activity.
   498  	ao := ActivityOptions{
   499  		ScheduleToStartTimeout: 10 * time.Second,
   500  		StartToCloseTimeout:    5 * time.Second,
   501  	}
   502  	ctx = WithActivityOptions(ctx, ao)
   503  
   504  	err := ExecuteActivity(ctx, testAct).Get(ctx, nil)
   505  	if err != nil {
   506  		return nil, err
   507  	}
   508  
   509  	// schedule a timer
   510  	err2 := Sleep(ctx, 1)
   511  	if err2 != nil {
   512  		return nil, err2
   513  	}
   514  
   515  	if ctx.Done().Receive(ctx, nil); ctx.Err() == ErrCanceled {
   516  		return []byte("Cancelled."), ctx.Err()
   517  	}
   518  	return []byte("Completed."), nil
   519  }
   520  
   521  func (s *WorkflowUnitTest) Test_CancelWorkflowAfterActivity() {
   522  	env := newTestWorkflowEnv(s.T())
   523  	env.RegisterDelayedCallback(func() {
   524  		env.CancelWorkflow()
   525  	}, time.Hour)
   526  	env.ExecuteWorkflow(cancelWorkflowAfterActivityTest)
   527  	s.True(env.IsWorkflowCompleted())
   528  }
   529  
   530  func signalWorkflowTest(ctx Context) ([]byte, error) {
   531  	// read multiple times.
   532  	var result string
   533  	ch := GetSignalChannel(ctx, "testSig1")
   534  	var v string
   535  	ok := ch.ReceiveAsync(&v)
   536  	if !ok {
   537  		return nil, errors.New("testSig1 not received")
   538  	}
   539  	result += v
   540  	ch.Receive(ctx, &v)
   541  	result += v
   542  
   543  	// Read on a selector.
   544  	ch2 := GetSignalChannel(ctx, "testSig2")
   545  	s := NewSelector(ctx)
   546  	s.AddReceive(ch2, func(c Channel, more bool) {
   547  		c.Receive(ctx, &v)
   548  		result += v
   549  	})
   550  	s.Select(ctx)
   551  	s.Select(ctx)
   552  	s.Select(ctx)
   553  
   554  	// Read on a selector inside the callback, multiple times.
   555  	ch2 = GetSignalChannel(ctx, "testSig2")
   556  	s = NewSelector(ctx)
   557  	s.AddReceive(ch2, func(c Channel, more bool) {
   558  		for i := 0; i < 4; i++ {
   559  			c.Receive(ctx, &v)
   560  			result += v
   561  		}
   562  	})
   563  	s.Select(ctx)
   564  
   565  	// Check un handled signals.
   566  	list := getWorkflowEnvOptions(ctx).getUnhandledSignalNames()
   567  	if len(list) != 1 || list[0] != "testSig3" {
   568  		panic("expecting one unhandled signal")
   569  	}
   570  	ch3 := GetSignalChannel(ctx, "testSig3")
   571  	ch3.Receive(ctx, &v)
   572  	result += v
   573  	list = getWorkflowEnvOptions(ctx).getUnhandledSignalNames()
   574  	if len(list) != 0 {
   575  		panic("expecting no unhandled signals")
   576  	}
   577  	return []byte(result), nil
   578  }
   579  
   580  func (s *WorkflowUnitTest) Test_SignalWorkflow() {
   581  	expected := []string{
   582  		"Sig1Value1;",
   583  		"Sig1Value2;",
   584  		"Sig2Value1;",
   585  		"Sig2Value2;",
   586  		"Sig2Value3;",
   587  		"Sig2Value4;",
   588  		"Sig2Value5;",
   589  		"Sig2Value6;",
   590  		"Sig2Value7;",
   591  		"Sig3Value1;",
   592  	}
   593  	env := newTestWorkflowEnv(s.T())
   594  
   595  	// Setup signals.
   596  	for i := 0; i < 2; i++ {
   597  		msg := expected[i]
   598  		var delay time.Duration
   599  		if i > 0 {
   600  			delay = time.Second
   601  		}
   602  		env.RegisterDelayedCallback(func() {
   603  			env.SignalWorkflow("testSig1", msg)
   604  		}, delay)
   605  	}
   606  	env.RegisterDelayedCallback(func() {
   607  		env.SignalWorkflow("testSig3", expected[9])
   608  	}, time.Hour)
   609  	for i := 2; i < 9; i++ {
   610  		msg := expected[i]
   611  		env.RegisterDelayedCallback(func() {
   612  			env.SignalWorkflow("testSig2", msg)
   613  		}, time.Hour)
   614  	}
   615  
   616  	env.ExecuteWorkflow(signalWorkflowTest)
   617  	s.True(env.IsWorkflowCompleted())
   618  	s.NoError(env.GetWorkflowError())
   619  	var result []byte
   620  	env.GetWorkflowResult(&result)
   621  	s.EqualValues(strings.Join(expected, ""), string(result))
   622  }
   623  
   624  type message struct {
   625  	Value string
   626  }
   627  
   628  func receiveCorruptSignalWorkflowTest(ctx Context) ([]message, error) {
   629  	ch := GetSignalChannel(ctx, "channelExpectingTypeMessage")
   630  	var result []message
   631  	var m message
   632  	ch.Receive(ctx, &m)
   633  	result = append(result, m)
   634  	return result, nil
   635  }
   636  
   637  func receiveCorruptSignalOnClosedChannelWorkflowTest(ctx Context) ([]message, error) {
   638  	ch := GetSignalChannel(ctx, "channelExpectingTypeMessage")
   639  	var result []message
   640  	var m message
   641  	ch.Close()
   642  	more := ch.Receive(ctx, &m)
   643  
   644  	result = append(result, message{Value: fmt.Sprintf("%v", more)})
   645  	return result, nil
   646  }
   647  
   648  func receiveWithSelectorCorruptSignalWorkflowTest(ctx Context) ([]message, error) {
   649  	var result []message
   650  
   651  	// Read on a selector
   652  	ch := GetSignalChannel(ctx, "channelExpectingTypeMessage")
   653  	s := NewSelector(ctx)
   654  	s.AddReceive(ch, func(c Channel, more bool) {
   655  		var m message
   656  		ch.Receive(ctx, &m)
   657  		result = append(result, m)
   658  	})
   659  	s.Select(ctx)
   660  	return result, nil
   661  }
   662  
   663  func receiveAsyncCorruptSignalOnClosedChannelWorkflowTest(ctx Context) ([]int, error) {
   664  	ch := GetSignalChannel(ctx, "channelExpectingInt")
   665  	var result []int
   666  	var m int
   667  
   668  	ch.SendAsync("wrong")
   669  	ch.Close()
   670  	ok := ch.ReceiveAsync(&m)
   671  	if ok == true {
   672  		result = append(result, m)
   673  	}
   674  
   675  	return result, nil
   676  }
   677  
   678  func receiveAsyncCorruptSignalWorkflowTest(ctx Context) ([]message, error) {
   679  	ch := GetSignalChannel(ctx, "channelExpectingTypeMessage")
   680  	var result []message
   681  	var m message
   682  
   683  	ch.SendAsync("wrong")
   684  	ok := ch.ReceiveAsync(&m)
   685  	if ok == true {
   686  		result = append(result, m)
   687  	}
   688  
   689  	ch.SendAsync("wrong again")
   690  	ch.SendAsync(message{
   691  		Value: "the right interface",
   692  	})
   693  	ok = ch.ReceiveAsync(&m)
   694  	if ok == true {
   695  		result = append(result, m)
   696  	}
   697  	return result, nil
   698  }
   699  
   700  func (s *WorkflowUnitTest) Test_CorruptedSignalWorkflow_ShouldLogMetricsAndNotPanic() {
   701  	scope, closer, reporter := metrics.NewTaggedMetricsScope()
   702  	s.SetMetricsScope(scope)
   703  	env := s.NewTestWorkflowEnvironment()
   704  	env.Test(s.T())
   705  
   706  	// Setup signals.
   707  	env.RegisterDelayedCallback(func() {
   708  		env.SignalWorkflow("channelExpectingTypeMessage", "wrong")
   709  	}, time.Millisecond)
   710  
   711  	env.RegisterDelayedCallback(func() {
   712  		env.SignalWorkflow("channelExpectingTypeMessage", message{
   713  			Value: "the right interface",
   714  		})
   715  	}, time.Second)
   716  
   717  	env.ExecuteWorkflow(receiveCorruptSignalWorkflowTest)
   718  	s.True(env.IsWorkflowCompleted())
   719  	s.NoError(env.GetWorkflowError())
   720  
   721  	var result []message
   722  	env.GetWorkflowResult(&result)
   723  
   724  	s.EqualValues(1, len(result))
   725  	s.EqualValues("the right interface", result[0].Value)
   726  
   727  	closer.Close()
   728  	counts := reporter.Counts()
   729  	s.EqualValues(1, len(counts))
   730  	s.EqualValues(metrics.CorruptedSignalsCounter, counts[0].Name())
   731  	s.EqualValues(1, counts[0].Value())
   732  }
   733  
   734  func (s *WorkflowUnitTest) Test_CorruptedSignalWorkflow_OnSelectorRead_ShouldLogMetricsAndNotPanic() {
   735  	scope, closer, reporter := metrics.NewTaggedMetricsScope()
   736  	s.SetMetricsScope(scope)
   737  	env := s.NewTestWorkflowEnvironment()
   738  	env.Test(s.T())
   739  
   740  	// Setup signals.
   741  	env.RegisterDelayedCallback(func() {
   742  		env.SignalWorkflow("channelExpectingTypeMessage", "wrong")
   743  	}, time.Second)
   744  
   745  	env.RegisterDelayedCallback(func() {
   746  		env.SignalWorkflow("channelExpectingTypeMessage", message{
   747  			Value: "the right interface",
   748  		})
   749  	}, 3*time.Second)
   750  
   751  	env.ExecuteWorkflow(receiveWithSelectorCorruptSignalWorkflowTest)
   752  	s.True(env.IsWorkflowCompleted())
   753  	s.NoError(env.GetWorkflowError())
   754  
   755  	var result []message
   756  	env.GetWorkflowResult(&result)
   757  
   758  	s.EqualValues(1, len(result))
   759  	s.EqualValues("the right interface", result[0].Value)
   760  
   761  	closer.Close()
   762  	counts := reporter.Counts()
   763  	s.EqualValues(1, len(counts))
   764  	s.EqualValues(metrics.CorruptedSignalsCounter, counts[0].Name())
   765  	s.EqualValues(1, counts[0].Value())
   766  }
   767  
   768  func (s *WorkflowUnitTest) Test_CorruptedSignalWorkflow_ReceiveAsync_ShouldLogMetricsAndNotPanic() {
   769  	scope, closer, reporter := metrics.NewTaggedMetricsScope()
   770  	s.SetMetricsScope(scope)
   771  	env := s.NewTestWorkflowEnvironment()
   772  	env.Test(s.T())
   773  
   774  	env.ExecuteWorkflow(receiveAsyncCorruptSignalWorkflowTest)
   775  	s.True(env.IsWorkflowCompleted())
   776  	s.NoError(env.GetWorkflowError())
   777  
   778  	var result []message
   779  	env.GetWorkflowResult(&result)
   780  	s.EqualValues(1, len(result))
   781  	s.EqualValues("the right interface", result[0].Value)
   782  
   783  	closer.Close()
   784  	counts := reporter.Counts()
   785  	s.EqualValues(1, len(counts))
   786  	s.EqualValues(metrics.CorruptedSignalsCounter, counts[0].Name())
   787  	s.EqualValues(2, counts[0].Value())
   788  }
   789  
   790  func (s *WorkflowUnitTest) Test_CorruptedSignalOnClosedChannelWorkflow_ReceiveAsync_ShouldComplete() {
   791  	env := newTestWorkflowEnv(s.T())
   792  
   793  	env.ExecuteWorkflow(receiveAsyncCorruptSignalOnClosedChannelWorkflowTest)
   794  	s.True(env.IsWorkflowCompleted())
   795  	s.NoError(env.GetWorkflowError())
   796  
   797  	var result []message
   798  	env.GetWorkflowResult(&result)
   799  	s.EqualValues(0, len(result))
   800  }
   801  
   802  func (s *WorkflowUnitTest) Test_CorruptedSignalOnClosedChannelWorkflow_Receive_ShouldComplete() {
   803  	env := newTestWorkflowEnv(s.T())
   804  
   805  	// Setup signals.
   806  	env.RegisterDelayedCallback(func() {
   807  		env.SignalWorkflow("channelExpectingTypeMessage", "wrong")
   808  	}, time.Second)
   809  
   810  	env.ExecuteWorkflow(receiveCorruptSignalOnClosedChannelWorkflowTest)
   811  	s.True(env.IsWorkflowCompleted())
   812  	s.NoError(env.GetWorkflowError())
   813  
   814  	var result []message
   815  	env.GetWorkflowResult(&result)
   816  	s.EqualValues(1, len(result))
   817  	s.Equal("false", result[0].Value)
   818  }
   819  
   820  func closeChannelTest(ctx Context) error {
   821  	ch := NewChannel(ctx)
   822  	Go(ctx, func(ctx Context) {
   823  		var dummy struct{}
   824  		ch.Receive(ctx, &dummy)
   825  		ch.Close()
   826  	})
   827  
   828  	ch.Send(ctx, struct{}{})
   829  	return nil
   830  }
   831  
   832  func (s *WorkflowUnitTest) Test_CloseChannelWorkflow() {
   833  	env := newTestWorkflowEnv(s.T())
   834  	env.ExecuteWorkflow(closeChannelTest)
   835  	s.True(env.IsWorkflowCompleted())
   836  	s.NoError(env.GetWorkflowError())
   837  }
   838  
   839  func closeChannelInSelectTest(ctx Context) error {
   840  	s := NewSelector(ctx)
   841  	sendCh := NewChannel(ctx)
   842  	receiveCh := NewChannel(ctx)
   843  	expectedValue := "expected value"
   844  
   845  	Go(ctx, func(ctx Context) {
   846  		sendCh.Close()
   847  		receiveCh.Send(ctx, expectedValue)
   848  	})
   849  
   850  	var v string
   851  	s.AddSend(sendCh, struct{}{}, func() {
   852  		panic("callback for sendCh should not be executed")
   853  	})
   854  	s.AddReceive(receiveCh, func(c Channel, m bool) {
   855  		c.Receive(ctx, &v)
   856  	})
   857  	s.Select(ctx)
   858  	if v != expectedValue {
   859  		panic("callback for receiveCh is not executed")
   860  	}
   861  	return nil
   862  }
   863  
   864  func (s *WorkflowUnitTest) Test_CloseChannelInSelectWorkflow() {
   865  	env := newTestWorkflowEnv(s.T())
   866  	env.ExecuteWorkflow(closeChannelInSelectTest)
   867  	s.True(env.IsWorkflowCompleted())
   868  	s.NoError(env.GetWorkflowError())
   869  }
   870  
   871  func bufferedChanWorkflowTest(ctx Context, bufferSize int) error {
   872  	bufferedCh := NewBufferedChannel(ctx, bufferSize)
   873  
   874  	Go(ctx, func(ctx Context) {
   875  		var dummy int
   876  		for i := 0; i < bufferSize; i++ {
   877  			bufferedCh.Receive(ctx, &dummy)
   878  		}
   879  	})
   880  
   881  	for i := 0; i < bufferSize+1; i++ {
   882  		bufferedCh.Send(ctx, i)
   883  	}
   884  	return nil
   885  }
   886  
   887  func (s *WorkflowUnitTest) Test_BufferedChanWorkflow() {
   888  	bufferSizeList := []int{1, 5}
   889  	for _, bufferSize := range bufferSizeList {
   890  		env := newTestWorkflowEnv(s.T())
   891  		env.ExecuteWorkflow(bufferedChanWorkflowTest, bufferSize)
   892  		s.True(env.IsWorkflowCompleted())
   893  		s.NoError(env.GetWorkflowError())
   894  	}
   895  }
   896  
   897  func bufferedChanWithSelectorWorkflowTest(ctx Context, bufferSize int) error {
   898  	bufferedCh := NewBufferedChannel(ctx, bufferSize)
   899  	selectedCh := NewChannel(ctx)
   900  	done := NewChannel(ctx)
   901  	var dummy struct{}
   902  
   903  	// 1. First we need to fill the buffer
   904  	for i := 0; i < bufferSize; i++ {
   905  		bufferedCh.Send(ctx, dummy)
   906  	}
   907  
   908  	// DO NOT change the order of these coroutines.
   909  	Go(ctx, func(ctx Context) {
   910  		// 3. Add another send callback to bufferedCh's blockedSends.
   911  		bufferedCh.Send(ctx, dummy)
   912  		done.Send(ctx, dummy)
   913  	})
   914  
   915  	Go(ctx, func(ctx Context) {
   916  		// 4.  Make sure selectedCh is selected
   917  		selectedCh.Receive(ctx, nil)
   918  
   919  		// 5. Get a value from channel buffer. Receive call will also check if there's any blocked sends.
   920  		// The first blockedSends is added by Select(). Since bufferedCh is not selected, it's fn() will
   921  		// return false. The Receive call should continue to check other blockedSends, until fn() returns
   922  		// true or the list is empty. In this case, it will move the value sent in step 3 into buffer
   923  		// and thus unblocks it.
   924  		bufferedCh.Receive(ctx, nil)
   925  	})
   926  
   927  	selector := NewSelector(ctx)
   928  	selector.AddSend(selectedCh, dummy, func() {})
   929  	selector.AddSend(bufferedCh, dummy, func() {})
   930  	// 2. When select is called, callback for the second send will be added to bufferedCh's blockedSends
   931  	selector.Select(ctx)
   932  
   933  	// Make sure no coroutine blocks
   934  	done.Receive(ctx, nil)
   935  	return nil
   936  }
   937  
   938  func (s *WorkflowUnitTest) Test_BufferedChanWithSelectorWorkflow() {
   939  	bufferSizeList := []int{1, 5}
   940  	for _, bufferSize := range bufferSizeList {
   941  		bufferSize := bufferSize
   942  		env := newTestWorkflowEnv(s.T())
   943  		env.ExecuteWorkflow(bufferedChanWithSelectorWorkflowTest, bufferSize)
   944  		s.True(env.IsWorkflowCompleted())
   945  		s.NoError(env.GetWorkflowError())
   946  	}
   947  }
   948  
   949  func activityOptionsWorkflow(ctx Context) (result string, err error) {
   950  	ao1 := ActivityOptions{
   951  		ActivityID: "id1",
   952  	}
   953  	ao2 := ActivityOptions{
   954  		ActivityID: "id2",
   955  	}
   956  	ctx1 := WithActivityOptions(ctx, ao1)
   957  	ctx2 := WithActivityOptions(ctx, ao2)
   958  
   959  	ctx1Ao := getActivityOptions(ctx1)
   960  	ctx2Ao := getActivityOptions(ctx2)
   961  	return *ctx1Ao.ActivityID + " " + *ctx2Ao.ActivityID, nil
   962  }
   963  
   964  // Test that activity options are correctly spawned with WithActivityOptions is called.
   965  // See https://github.com/uber-go/cadence-client/issues/372
   966  func (s *WorkflowUnitTest) Test_ActivityOptionsWorkflow() {
   967  	env := newTestWorkflowEnv(s.T())
   968  	env.ExecuteWorkflow(activityOptionsWorkflow)
   969  	s.True(env.IsWorkflowCompleted())
   970  	s.NoError(env.GetWorkflowError())
   971  	var result string
   972  	env.GetWorkflowResult(&result)
   973  	s.Equal("id1 id2", result)
   974  }
   975  
   976  const (
   977  	memoTestKey = "testKey"
   978  	memoTestVal = "testVal"
   979  )
   980  
   981  func getMemoTest(ctx Context) (result string, err error) {
   982  	info := GetWorkflowInfo(ctx)
   983  	val, ok := info.Memo.Fields[memoTestKey]
   984  	if !ok {
   985  		return "", errors.New("no memo found")
   986  	}
   987  	err = NewValue(val).Get(&result)
   988  	return result, err
   989  }
   990  
   991  func (s *WorkflowUnitTest) Test_MemoWorkflow() {
   992  	env := newTestWorkflowEnv(s.T())
   993  	memo := map[string]interface{}{
   994  		memoTestKey: memoTestVal,
   995  	}
   996  	err := env.SetMemoOnStart(memo)
   997  	s.NoError(err)
   998  
   999  	env.ExecuteWorkflow(getMemoTest)
  1000  	s.True(env.IsWorkflowCompleted())
  1001  	s.NoError(env.GetWorkflowError())
  1002  	var result string
  1003  	env.GetWorkflowResult(&result)
  1004  	s.Equal(memoTestVal, result)
  1005  }
  1006  
  1007  func sleepWorkflow(ctx Context, input time.Duration) (int, error) {
  1008  	if err := Sleep(ctx, input); err != nil {
  1009  		return 0, err
  1010  	}
  1011  
  1012  	return 1, nil
  1013  }
  1014  
  1015  func waitGroupWorkflowTest(ctx Context, n int) (int, error) {
  1016  	ctx = WithChildWorkflowOptions(ctx, ChildWorkflowOptions{
  1017  		ExecutionStartToCloseTimeout: time.Second * 30,
  1018  	})
  1019  
  1020  	var err error
  1021  	results := make([]int, 0, n)
  1022  	waitGroup := NewWaitGroup(ctx)
  1023  	for i := 0; i < n; i++ {
  1024  		waitGroup.Add(1)
  1025  		t := time.Second * time.Duration(i+1)
  1026  		Go(ctx, func(ctx Context) {
  1027  			var result int
  1028  			err = ExecuteChildWorkflow(ctx, sleepWorkflow, t).Get(ctx, &result)
  1029  			results = append(results, result)
  1030  			waitGroup.Done()
  1031  		})
  1032  	}
  1033  
  1034  	waitGroup.Wait(ctx)
  1035  	if err != nil {
  1036  		return 0, err
  1037  	}
  1038  
  1039  	sum := 0
  1040  	for _, v := range results {
  1041  		sum = sum + v
  1042  	}
  1043  
  1044  	return sum, nil
  1045  }
  1046  
  1047  func waitGroupWaitForMWorkflowTest(ctx Context, n int, m int) (int, error) {
  1048  	ctx = WithChildWorkflowOptions(ctx, ChildWorkflowOptions{
  1049  		ExecutionStartToCloseTimeout: time.Second * 30,
  1050  	})
  1051  
  1052  	var err error
  1053  	results := make([]int, 0, n)
  1054  	waitGroup := NewWaitGroup(ctx)
  1055  	waitGroup.Add(m)
  1056  	for i := 0; i < n; i++ {
  1057  		t := time.Second * time.Duration(i+1)
  1058  		Go(ctx, func(ctx Context) {
  1059  			var result int
  1060  			err = ExecuteChildWorkflow(ctx, sleepWorkflow, t).Get(ctx, &result)
  1061  			results = append(results, result)
  1062  			waitGroup.Done()
  1063  		})
  1064  	}
  1065  
  1066  	waitGroup.Wait(ctx)
  1067  	if err != nil {
  1068  		return 0, err
  1069  	}
  1070  
  1071  	sum := 0
  1072  	for _, v := range results {
  1073  		sum = sum + v
  1074  	}
  1075  
  1076  	return sum, nil
  1077  }
  1078  
  1079  func waitGroupMultipleWaitsWorkflowTest(ctx Context) (int, error) {
  1080  	ctx = WithChildWorkflowOptions(ctx, ChildWorkflowOptions{
  1081  		ExecutionStartToCloseTimeout: time.Second * 30,
  1082  	})
  1083  
  1084  	n := 10
  1085  	var err error
  1086  	results := make([]int, 0, n)
  1087  	waitGroup := NewWaitGroup(ctx)
  1088  	waitGroup.Add(4)
  1089  	for i := 0; i < n; i++ {
  1090  		t := time.Second * time.Duration(i+1)
  1091  		Go(ctx, func(ctx Context) {
  1092  			var result int
  1093  			err = ExecuteChildWorkflow(ctx, sleepWorkflow, t).Get(ctx, &result)
  1094  			results = append(results, result)
  1095  			waitGroup.Done()
  1096  		})
  1097  	}
  1098  
  1099  	waitGroup.Wait(ctx)
  1100  	if err != nil {
  1101  		return 0, err
  1102  	}
  1103  
  1104  	waitGroup.Add(6)
  1105  	waitGroup.Wait(ctx)
  1106  	if err != nil {
  1107  		return 0, err
  1108  	}
  1109  
  1110  	sum := 0
  1111  	for _, v := range results {
  1112  		sum = sum + v
  1113  	}
  1114  
  1115  	return sum, nil
  1116  }
  1117  
  1118  func waitGroupMultipleConcurrentWaitsPanicsWorkflowTest(ctx Context) (int, error) {
  1119  	ctx = WithChildWorkflowOptions(ctx, ChildWorkflowOptions{
  1120  		ExecutionStartToCloseTimeout: time.Second * 30,
  1121  	})
  1122  
  1123  	var err error
  1124  	var result1 int
  1125  	var result2 int
  1126  
  1127  	waitGroup := NewWaitGroup(ctx)
  1128  	waitGroup.Add(2)
  1129  
  1130  	Go(ctx, func(ctx Context) {
  1131  		err = ExecuteChildWorkflow(ctx, sleepWorkflow, time.Second*5).Get(ctx, &result1)
  1132  		waitGroup.Done()
  1133  	})
  1134  
  1135  	Go(ctx, func(ctx Context) {
  1136  		err = ExecuteChildWorkflow(ctx, sleepWorkflow, time.Second*10).Get(ctx, &result2)
  1137  		waitGroup.Wait(ctx)
  1138  	})
  1139  
  1140  	waitGroup.Wait(ctx)
  1141  	if err != nil {
  1142  		return 0, err
  1143  	}
  1144  
  1145  	return result1 + result2, nil
  1146  }
  1147  
  1148  func waitGroupNegativeCounterPanicsWorkflowTest(ctx Context) (int, error) {
  1149  	ctx = WithChildWorkflowOptions(ctx, ChildWorkflowOptions{
  1150  		ExecutionStartToCloseTimeout: time.Second * 30,
  1151  	})
  1152  
  1153  	var err error
  1154  	var result int
  1155  	waitGroup := NewWaitGroup(ctx)
  1156  
  1157  	Go(ctx, func(ctx Context) {
  1158  		waitGroup.Done()
  1159  		err = ExecuteChildWorkflow(ctx, sleepWorkflow, time.Second*5).Get(ctx, &result)
  1160  	})
  1161  
  1162  	waitGroup.Wait(ctx)
  1163  	if err != nil {
  1164  		return 0, err
  1165  	}
  1166  
  1167  	return result, nil
  1168  }
  1169  
  1170  func (s *WorkflowUnitTest) Test_waitGroupNegativeCounterPanicsWorkflowTest() {
  1171  	env := newTestWorkflowEnv(s.T())
  1172  	env.RegisterWorkflow(waitGroupNegativeCounterPanicsWorkflowTest)
  1173  	env.ExecuteWorkflow(waitGroupNegativeCounterPanicsWorkflowTest)
  1174  	s.True(env.IsWorkflowCompleted())
  1175  
  1176  	resultErr := env.GetWorkflowError().(*PanicError)
  1177  	s.EqualValues("negative WaitGroup counter", resultErr.Error())
  1178  	s.Contains(resultErr.StackTrace(), "cadence/internal.waitGroupNegativeCounterPanicsWorkflowTest")
  1179  }
  1180  
  1181  func (s *WorkflowUnitTest) Test_WaitGroupMultipleConcurrentWaitsPanicsWorkflowTest() {
  1182  	env := newTestWorkflowEnv(s.T())
  1183  	env.RegisterWorkflow(waitGroupMultipleConcurrentWaitsPanicsWorkflowTest)
  1184  	env.RegisterWorkflow(sleepWorkflow)
  1185  	env.ExecuteWorkflow(waitGroupMultipleConcurrentWaitsPanicsWorkflowTest)
  1186  	s.True(env.IsWorkflowCompleted())
  1187  
  1188  	resultErr := env.GetWorkflowError().(*PanicError)
  1189  	s.EqualValues("WaitGroup is reused before previous Wait has returned", resultErr.Error())
  1190  	s.Contains(resultErr.StackTrace(), "cadence/internal.waitGroupMultipleConcurrentWaitsPanicsWorkflowTest")
  1191  }
  1192  
  1193  func (s *WorkflowUnitTest) Test_WaitGroupMultipleWaitsWorkflowTest() {
  1194  	env := newTestWorkflowEnv(s.T())
  1195  	env.RegisterWorkflow(waitGroupMultipleWaitsWorkflowTest)
  1196  	env.RegisterWorkflow(sleepWorkflow)
  1197  	env.ExecuteWorkflow(waitGroupMultipleWaitsWorkflowTest)
  1198  	s.True(env.IsWorkflowCompleted())
  1199  	s.NoError(env.GetWorkflowError())
  1200  
  1201  	var total int
  1202  	env.GetWorkflowResult(&total)
  1203  	s.Equal(10, total)
  1204  }
  1205  
  1206  func (s *WorkflowUnitTest) Test_WaitGroupWaitForMWorkflowTest() {
  1207  	env := newTestWorkflowEnv(s.T())
  1208  	env.RegisterWorkflow(waitGroupWaitForMWorkflowTest)
  1209  	env.RegisterWorkflow(sleepWorkflow)
  1210  
  1211  	n := 10
  1212  	m := 5
  1213  	env.ExecuteWorkflow(waitGroupWaitForMWorkflowTest, n, m)
  1214  	s.True(env.IsWorkflowCompleted())
  1215  	s.NoError(env.GetWorkflowError())
  1216  
  1217  	var total int
  1218  	env.GetWorkflowResult(&total)
  1219  	s.Equal(m, total)
  1220  }
  1221  
  1222  func (s *WorkflowUnitTest) Test_WaitGroupWorkflowTest() {
  1223  	env := newTestWorkflowEnv(s.T())
  1224  	env.RegisterWorkflow(waitGroupWorkflowTest)
  1225  	env.RegisterWorkflow(sleepWorkflow)
  1226  
  1227  	n := 10
  1228  	env.ExecuteWorkflow(waitGroupWorkflowTest, n)
  1229  	s.True(env.IsWorkflowCompleted())
  1230  	s.Nil(env.GetWorkflowError())
  1231  	s.NoError(env.GetWorkflowError())
  1232  
  1233  	var total int
  1234  	env.GetWorkflowResult(&total)
  1235  	s.Equal(n, total)
  1236  }
  1237  
  1238  func (s *WorkflowUnitTest) Test_StaleGoroutinesAreShutDown() {
  1239  	env := newTestWorkflowEnv(s.T())
  1240  	deferred := make(chan struct{})
  1241  	after := make(chan struct{})
  1242  	wf := func(ctx Context) error {
  1243  		Go(ctx, func(ctx Context) {
  1244  			defer func() { close(deferred) }()
  1245  			_ = Sleep(ctx, time.Hour) // outlive the workflow
  1246  			close(after)
  1247  		})
  1248  		_ = Sleep(ctx, time.Minute)
  1249  		return nil
  1250  	}
  1251  	env.RegisterWorkflow(wf)
  1252  
  1253  	env.ExecuteWorkflow(wf)
  1254  	s.True(env.IsWorkflowCompleted())
  1255  	s.NoError(env.GetWorkflowError())
  1256  
  1257  	// goroutines are shut down async at the moment, so wait with a timeout.
  1258  	// give it up to 1s total.
  1259  
  1260  	started := time.Now()
  1261  	maxWait := time.NewTimer(time.Second)
  1262  	defer maxWait.Stop()
  1263  	select {
  1264  	case <-deferred:
  1265  		s.T().Logf("deferred callback executed after %v", time.Now().Sub(started))
  1266  	case <-maxWait.C:
  1267  		s.Fail("deferred func should have been called within 1 second")
  1268  	}
  1269  	// if deferred code has run, this has already occurred-or-not.
  1270  	// if it timed out waiting for the deferred code, it has waited long enough, and this is mostly a curiosity.
  1271  	select {
  1272  	case <-after:
  1273  		s.Fail("code after sleep should not have run")
  1274  	default:
  1275  		s.T().Log("code after sleep correctly not executed")
  1276  	}
  1277  }
  1278  
  1279  var _ WorkflowInterceptorFactory = (*tracingInterceptorFactory)(nil)
  1280  
  1281  type tracingInterceptorFactory struct {
  1282  	instances []*tracingInterceptor
  1283  }
  1284  
  1285  func (t *tracingInterceptorFactory) NewInterceptor(info *WorkflowInfo, next WorkflowInterceptor) WorkflowInterceptor {
  1286  	result := &tracingInterceptor{
  1287  		WorkflowInterceptorBase: WorkflowInterceptorBase{Next: next},
  1288  	}
  1289  	t.instances = append(t.instances, result)
  1290  	return result
  1291  }
  1292  
  1293  var _ WorkflowInterceptor = (*tracingInterceptor)(nil)
  1294  
  1295  type tracingInterceptor struct {
  1296  	WorkflowInterceptorBase
  1297  	trace []string
  1298  }
  1299  
  1300  func (t *tracingInterceptor) ExecuteActivity(ctx Context, activityType string, args ...interface{}) Future {
  1301  	t.trace = append(t.trace, "ExecuteActivity "+activityType)
  1302  	return t.Next.ExecuteActivity(ctx, activityType, args...)
  1303  }
  1304  
  1305  func (t *tracingInterceptor) ExecuteWorkflow(ctx Context, workflowType string, args ...interface{}) []interface{} {
  1306  	t.trace = append(t.trace, "ExecuteWorkflow "+workflowType+" begin")
  1307  	result := t.Next.ExecuteWorkflow(ctx, workflowType, args...)
  1308  	t.trace = append(t.trace, "ExecuteWorkflow "+workflowType+" end")
  1309  	return result
  1310  }
  1311  
  1312  type WorkflowOptionTest struct {
  1313  	suite.Suite
  1314  }
  1315  
  1316  func TestWorkflowOption(t *testing.T) {
  1317  	suite.Run(t, new(WorkflowOptionTest))
  1318  }
  1319  
  1320  func (t *WorkflowOptionTest) TestKnowQueryType_NoHandlers() {
  1321  	wo := workflowOptions{queryHandlers: make(map[string]func([]byte) ([]byte, error))}
  1322  	t.ElementsMatch(
  1323  		[]string{
  1324  			QueryTypeStackTrace,
  1325  			QueryTypeOpenSessions,
  1326  			QueryTypeQueryTypes,
  1327  		},
  1328  		wo.KnownQueryTypes())
  1329  }
  1330  
  1331  func (t *WorkflowOptionTest) TestKnowQueryType_WithHandlers() {
  1332  	wo := workflowOptions{queryHandlers: map[string]func([]byte) ([]byte, error){
  1333  		"a": nil,
  1334  		"b": nil,
  1335  	}}
  1336  
  1337  	t.ElementsMatch(
  1338  		[]string{
  1339  			QueryTypeStackTrace,
  1340  			QueryTypeOpenSessions,
  1341  			QueryTypeQueryTypes,
  1342  			"a",
  1343  			"b",
  1344  		},
  1345  		wo.KnownQueryTypes())
  1346  }