go.uber.org/cadence@v1.2.9/internal/internal_coroutines_test.go (about) 1 // Copyright (c) 2017-2020 Uber Technologies Inc. 2 // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 package internal 23 24 import ( 25 "errors" 26 "fmt" 27 "runtime" 28 "strings" 29 "testing" 30 "time" 31 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 ) 35 36 func createRootTestContext(t *testing.T) (ctx Context) { 37 env := newTestWorkflowEnv(t) 38 interceptors, envInterceptor := newWorkflowInterceptors(env.impl, env.impl.workflowInterceptors) 39 return newWorkflowContext(env.impl, interceptors, envInterceptor) 40 } 41 42 func TestDispatcher(t *testing.T) { 43 value := "foo" 44 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { value = "bar" }) 45 require.Equal(t, "foo", value) 46 require.NoError(t, d.ExecuteUntilAllBlocked()) 47 require.True(t, d.IsDone()) 48 require.Equal(t, "bar", value) 49 } 50 51 func TestNonBlockingChildren(t *testing.T) { 52 var history []string 53 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 54 for i := 0; i < 10; i++ { 55 ii := i 56 Go(ctx, func(ctx Context) { 57 history = append(history, fmt.Sprintf("child-%v", ii)) 58 }) 59 } 60 history = append(history, "root") 61 }) 62 require.EqualValues(t, 0, len(history)) 63 64 require.NoError(t, d.ExecuteUntilAllBlocked()) 65 require.True(t, d.IsDone()) 66 require.EqualValues(t, 11, len(history)) 67 } 68 69 func TestNonbufferedChannel(t *testing.T) { 70 var history []string 71 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 72 c1 := NewChannel(ctx) 73 Go(ctx, func(ctx Context) { 74 history = append(history, "child-start") 75 var v string 76 more := c1.Receive(ctx, &v) 77 require.True(t, more) 78 history = append(history, fmt.Sprintf("child-end-%v", v)) 79 }) 80 history = append(history, "root-before-channel-put") 81 c1.Send(ctx, "value1") 82 history = append(history, "root-after-channel-put") 83 84 }) 85 require.EqualValues(t, 0, len(history)) 86 require.NoError(t, d.ExecuteUntilAllBlocked()) 87 require.True(t, d.IsDone()) 88 89 expected := []string{ 90 "root-before-channel-put", 91 "child-start", 92 "child-end-value1", 93 "root-after-channel-put", 94 } 95 require.EqualValues(t, expected, history) 96 97 } 98 99 func TestNonbufferedChannelBlockedReceive(t *testing.T) { 100 var history []string 101 var c2 Channel 102 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 103 c1 := NewChannel(ctx) 104 c2 = NewChannel(ctx) 105 Go(ctx, func(ctx Context) { 106 var v string 107 more := c1.Receive(ctx, &v) 108 require.True(t, more) 109 history = append(history, fmt.Sprintf("child1-end1-%v", v)) 110 more = c1.Receive(ctx, &v) 111 require.True(t, more) 112 history = append(history, fmt.Sprintf("child1-end2-%v", v)) 113 }) 114 Go(ctx, func(ctx Context) { 115 var v string 116 history = append(history, "child2-start") 117 more := c2.Receive(ctx, &v) 118 require.True(t, more) 119 history = append(history, fmt.Sprintf("child2-end1-%v", v)) 120 more = c2.Receive(ctx, &v) 121 require.True(t, more) 122 history = append(history, fmt.Sprintf("child2-end2-%v", v)) 123 }) 124 125 history = append(history, "root-before-channel-put") 126 c1.Send(ctx, "value11") 127 c1.Send(ctx, "value12") 128 history = append(history, "root-after-channel-put") 129 130 }) 131 require.EqualValues(t, 0, len(history)) 132 require.NoError(t, d.ExecuteUntilAllBlocked()) 133 c2.SendAsync("value21") 134 require.NoError(t, d.ExecuteUntilAllBlocked()) 135 c2.SendAsync("value22") 136 require.NoError(t, d.ExecuteUntilAllBlocked()) 137 require.True(t, d.IsDone(), d.StackTrace()) 138 } 139 140 func TestBufferedChannelPut(t *testing.T) { 141 var history []string 142 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 143 c1 := NewBufferedChannel(ctx, 1) 144 Go(ctx, func(ctx Context) { 145 history = append(history, "child-start") 146 var v1, v2 string 147 more := c1.Receive(ctx, &v1) 148 require.True(t, more) 149 history = append(history, fmt.Sprintf("child-end-%v", v1)) 150 c1.Receive(ctx, &v2) 151 history = append(history, fmt.Sprintf("child-end-%v", v2)) 152 153 }) 154 history = append(history, "root-before-channel-put") 155 c1.Send(ctx, "value1") 156 history = append(history, "root-after-channel-put1") 157 c1.Send(ctx, "value2") 158 history = append(history, "root-after-channel-put2") 159 }) 160 require.EqualValues(t, 0, len(history)) 161 require.NoError(t, d.ExecuteUntilAllBlocked()) 162 require.True(t, d.IsDone()) 163 164 expected := []string{ 165 "root-before-channel-put", 166 "root-after-channel-put1", 167 "child-start", 168 "child-end-value1", 169 "child-end-value2", 170 "root-after-channel-put2", 171 } 172 require.EqualValues(t, expected, history) 173 } 174 175 func TestBufferedChannelGet(t *testing.T) { 176 var history []string 177 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 178 c1 := NewChannel(ctx) 179 c2 := NewBufferedChannel(ctx, 2) 180 181 Go(ctx, func(ctx Context) { 182 history = append(history, "child1-start") 183 c2.Send(ctx, "bar1") 184 history = append(history, "child1-get") 185 var v1 string 186 more := c1.Receive(ctx, &v1) 187 require.True(t, more) 188 history = append(history, fmt.Sprintf("child1-end-%v", v1)) 189 190 }) 191 Go(ctx, func(ctx Context) { 192 history = append(history, "child2-start") 193 c2.Send(ctx, "bar2") 194 history = append(history, "child2-get") 195 var v1 string 196 more := c1.Receive(ctx, &v1) 197 require.True(t, more) 198 history = append(history, fmt.Sprintf("child2-end-%v", v1)) 199 }) 200 history = append(history, "root-before-channel-get1") 201 c2.Receive(ctx, nil) 202 history = append(history, "root-before-channel-get2") 203 c2.Receive(ctx, nil) 204 history = append(history, "root-before-channel-put") 205 c1.Send(ctx, "value1") 206 history = append(history, "root-after-channel-put1") 207 c1.Send(ctx, "value2") 208 history = append(history, "root-after-channel-put2") 209 }) 210 require.EqualValues(t, 0, len(history)) 211 require.NoError(t, d.ExecuteUntilAllBlocked()) 212 require.True(t, d.IsDone(), strings.Join(history, "\n")+"\n\n"+d.StackTrace()) 213 214 expected := []string{ 215 "root-before-channel-get1", 216 "child1-start", 217 "child1-get", 218 "child2-start", 219 "child2-get", 220 "root-before-channel-get2", 221 "root-before-channel-put", 222 "root-after-channel-put1", 223 "root-after-channel-put2", 224 "child1-end-value1", 225 "child2-end-value2", 226 } 227 require.EqualValues(t, expected, history) 228 } 229 230 func TestNotBlockingSelect(t *testing.T) { 231 var history []string 232 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 233 c1 := NewBufferedChannel(ctx, 1) 234 c2 := NewBufferedChannel(ctx, 1) 235 s := NewSelector(ctx) 236 s. 237 AddReceive(c1, func(c Channel, more bool) { 238 require.True(t, more) 239 var v string 240 c.Receive(ctx, &v) 241 history = append(history, fmt.Sprintf("c1-%v", v)) 242 }). 243 AddReceive(c2, func(c Channel, more bool) { 244 require.True(t, more) 245 var v string 246 c.Receive(ctx, &v) 247 history = append(history, fmt.Sprintf("c2-%v", v)) 248 }). 249 AddDefault(func() { history = append(history, "default") }) 250 c1.Send(ctx, "one") 251 s.Select(ctx) 252 c2.Send(ctx, "two") 253 s.Select(ctx) 254 s.Select(ctx) 255 }) 256 require.NoError(t, d.ExecuteUntilAllBlocked()) 257 require.True(t, d.IsDone()) 258 259 expected := []string{ 260 "c1-one", 261 "c2-two", 262 "default", 263 } 264 require.EqualValues(t, expected, history) 265 } 266 267 func TestBlockingSelect(t *testing.T) { 268 var history []string 269 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 270 c1 := NewChannel(ctx) 271 c2 := NewChannel(ctx) 272 Go(ctx, func(ctx Context) { 273 history = append(history, "add-one") 274 c1.Send(ctx, "one") 275 history = append(history, "add-one-done") 276 277 }) 278 Go(ctx, func(ctx Context) { 279 history = append(history, "add-two") 280 c2.Send(ctx, "two") 281 history = append(history, "add-two-done") 282 }) 283 284 s := NewSelector(ctx) 285 s. 286 AddReceive(c1, func(c Channel, more bool) { 287 require.True(t, more) 288 var v string 289 c.Receive(ctx, &v) 290 history = append(history, fmt.Sprintf("c1-%v", v)) 291 }). 292 AddReceive(c2, func(c Channel, more bool) { 293 var v string 294 c.Receive(ctx, &v) 295 history = append(history, fmt.Sprintf("c2-%v", v)) 296 }) 297 history = append(history, "select1") 298 s.Select(ctx) 299 history = append(history, "select2") 300 s.Select(ctx) 301 history = append(history, "done") 302 }) 303 require.NoError(t, d.ExecuteUntilAllBlocked()) 304 require.True(t, d.IsDone(), strings.Join(history, "\n")) 305 306 expected := []string{ 307 "select1", 308 "add-one", 309 "add-one-done", 310 "add-two", 311 "c1-one", 312 "select2", 313 "c2-two", 314 "done", 315 "add-two-done", 316 } 317 require.EqualValues(t, expected, history) 318 } 319 320 func TestBlockingSelectAsyncSend(t *testing.T) { 321 var history []string 322 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 323 324 c1 := NewChannel(ctx) 325 s := NewSelector(ctx) 326 s. 327 AddReceive(c1, func(c Channel, more bool) { 328 require.True(t, more) 329 var v int 330 c.Receive(ctx, &v) 331 history = append(history, fmt.Sprintf("c1-%v", v)) 332 }) 333 for i := 0; i < 3; i++ { 334 ii := i // to reference within closure 335 Go(ctx, func(ctx Context) { 336 history = append(history, fmt.Sprintf("add-%v", ii)) 337 c1.SendAsync(ii) 338 }) 339 history = append(history, fmt.Sprintf("select-%v", ii)) 340 s.Select(ctx) 341 } 342 history = append(history, "done") 343 }) 344 require.NoError(t, d.ExecuteUntilAllBlocked()) 345 require.True(t, d.IsDone(), strings.Join(history, "\n")) 346 347 expected := []string{ 348 "select-0", 349 "add-0", 350 "c1-0", 351 "select-1", 352 "add-1", 353 "c1-1", 354 "select-2", 355 "add-2", 356 "c1-2", 357 "done", 358 } 359 require.EqualValues(t, expected, history) 360 } 361 362 func TestSelectOnClosedChannel(t *testing.T) { 363 var history []string 364 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 365 c := NewBufferedChannel(ctx, 1) 366 c.Send(ctx, 5) 367 c.Close() 368 369 selector := NewNamedSelector(ctx, "waiting for channel") 370 371 selector.AddReceive(c, func(f Channel, more bool) { 372 var n int 373 374 if !more { 375 history = append(history, "more from function is false") 376 return 377 } 378 379 more = f.Receive(ctx, &n) 380 if !more { 381 history = append(history, "more from receive is false") 382 return 383 } 384 history = append(history, fmt.Sprintf("got message on channel: %v", n)) 385 }) 386 387 selector.Select(ctx) 388 selector.Select(ctx) 389 selector.Select(ctx) 390 selector.Select(ctx) 391 }) 392 require.NoError(t, d.ExecuteUntilAllBlocked()) 393 require.True(t, d.IsDone(), strings.Join(history, "\n")) 394 395 expected := []string{ 396 "got message on channel: 5", 397 "more from function is false", 398 "more from function is false", 399 "more from function is false", 400 } 401 require.EqualValues(t, expected, history) 402 } 403 404 func TestBlockingSelectAsyncSend2(t *testing.T) { 405 var history []string 406 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 407 c1 := NewBufferedChannel(ctx, 100) 408 c2 := NewBufferedChannel(ctx, 100) 409 s := NewSelector(ctx) 410 s. 411 AddReceive(c1, func(c Channel, more bool) { 412 require.True(t, more) 413 var v string 414 c.Receive(ctx, &v) 415 history = append(history, fmt.Sprintf("c1-%v", v)) 416 }). 417 AddReceive(c2, func(c Channel, more bool) { 418 require.True(t, more) 419 var v string 420 c.Receive(ctx, &v) 421 history = append(history, fmt.Sprintf("c2-%v", v)) 422 }) 423 424 history = append(history, "send-s2") 425 c2.SendAsync("s2") 426 history = append(history, "select-0") 427 s.Select(ctx) 428 history = append(history, "send-s1") 429 c1.SendAsync("s1") 430 history = append(history, "select-1") 431 s.Select(ctx) 432 history = append(history, "done") 433 }) 434 require.NoError(t, d.ExecuteUntilAllBlocked()) 435 require.True(t, d.IsDone(), strings.Join(history, "\n")) 436 437 expected := []string{ 438 "send-s2", 439 "select-0", 440 "c2-s2", 441 "send-s1", 442 "select-1", 443 "c1-s1", 444 "done", 445 } 446 require.EqualValues(t, expected, history) 447 } 448 449 func TestSendSelect(t *testing.T) { 450 var history []string 451 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 452 c1 := NewChannel(ctx) 453 c2 := NewChannel(ctx) 454 Go(ctx, func(ctx Context) { 455 history = append(history, "receiver") 456 var v string 457 more := c2.Receive(ctx, &v) 458 require.True(t, more) 459 history = append(history, fmt.Sprintf("c2-%v", v)) 460 more = c1.Receive(ctx, &v) 461 462 require.True(t, more) 463 history = append(history, fmt.Sprintf("c1-%v", v)) 464 }) 465 s := NewSelector(ctx) 466 s.AddSend(c1, "one", func() { history = append(history, "send1") }). 467 AddSend(c2, "two", func() { history = append(history, "send2") }) 468 history = append(history, "select1") 469 s.Select(ctx) 470 history = append(history, "select2") 471 s.Select(ctx) 472 history = append(history, "done") 473 }) 474 require.NoError(t, d.ExecuteUntilAllBlocked()) 475 require.True(t, d.IsDone()) 476 477 expected := []string{ 478 "select1", 479 "receiver", 480 "c2-two", 481 "send2", 482 "select2", 483 "send1", 484 "done", 485 "c1-one", 486 } 487 require.EqualValues(t, expected, history) 488 } 489 490 func TestSendSelectWithAsyncReceive(t *testing.T) { 491 var history []string 492 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 493 c1 := NewChannel(ctx) 494 c2 := NewChannel(ctx) 495 Go(ctx, func(ctx Context) { 496 history = append(history, "receiver") 497 var v string 498 ok, more := c2.ReceiveAsyncWithMoreFlag(&v) 499 require.True(t, ok) 500 require.True(t, more) 501 history = append(history, fmt.Sprintf("c2-%v", v)) 502 more = c1.Receive(ctx, &v) 503 504 require.True(t, more) 505 history = append(history, fmt.Sprintf("c1-%v", v)) 506 }) 507 s := NewSelector(ctx) 508 s.AddSend(c1, "one", func() { history = append(history, "send1") }). 509 AddSend(c2, "two", func() { history = append(history, "send2") }) 510 history = append(history, "select1") 511 s.Select(ctx) 512 history = append(history, "select2") 513 s.Select(ctx) 514 history = append(history, "done") 515 }) 516 require.NoError(t, d.ExecuteUntilAllBlocked()) 517 require.True(t, d.IsDone(), strings.Join(history, "\n")) 518 519 expected := []string{ 520 "select1", 521 "receiver", 522 "c2-two", 523 "send2", 524 "select2", 525 "send1", 526 "done", 527 "c1-one", 528 } 529 require.EqualValues(t, expected, history) 530 } 531 532 func TestChannelClose(t *testing.T) { 533 var history []string 534 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 535 jobs := NewBufferedChannel(ctx, 5) 536 done := NewNamedChannel(ctx, "done") 537 538 GoNamed(ctx, "receiver", func(ctx Context) { 539 for { 540 var j int 541 more := jobs.Receive(ctx, &j) 542 if more { 543 history = append(history, fmt.Sprintf("received job %v", j)) 544 } else { 545 history = append(history, "received all jobs") 546 done.Send(ctx, true) 547 return 548 } 549 } 550 }) 551 for j := 1; j <= 3; j++ { 552 jobs.Send(ctx, j) 553 history = append(history, fmt.Sprintf("sent job %v", j)) 554 } 555 jobs.Close() 556 history = append(history, "sent all jobs") 557 done.Receive(ctx, nil) 558 history = append(history, "done") 559 560 }) 561 require.EqualValues(t, 0, len(history)) 562 require.NoError(t, d.ExecuteUntilAllBlocked()) 563 require.True(t, d.IsDone(), d.StackTrace()) 564 565 expected := []string{ 566 "sent job 1", 567 "sent job 2", 568 "sent job 3", 569 "sent all jobs", 570 "received job 1", 571 "received job 2", 572 "received job 3", 573 "received all jobs", 574 "done", 575 } 576 require.EqualValues(t, expected, history) 577 } 578 579 func TestSendClosedChannel(t *testing.T) { 580 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 581 defer func() { 582 require.NotNil(t, recover(), "panic expected") 583 }() 584 c := NewChannel(ctx) 585 Go(ctx, func(ctx Context) { 586 c.Close() 587 }) 588 c.Send(ctx, "baz") 589 }) 590 require.NoError(t, d.ExecuteUntilAllBlocked()) 591 require.True(t, d.IsDone()) 592 } 593 594 func TestBlockedSendClosedChannel(t *testing.T) { 595 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 596 defer func() { 597 require.NotNil(t, recover(), "panic expected") 598 }() 599 c := NewBufferedChannel(ctx, 5) 600 c.Send(ctx, "bar") 601 c.Close() 602 c.Send(ctx, "baz") 603 }) 604 require.NoError(t, d.ExecuteUntilAllBlocked()) 605 require.True(t, d.IsDone()) 606 } 607 608 func TestAsyncSendClosedChannel(t *testing.T) { 609 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 610 defer func() { 611 require.NotNil(t, recover(), "panic expected") 612 }() 613 c := NewBufferedChannel(ctx, 5) 614 c.Send(ctx, "bar") 615 c.Close() 616 _ = c.SendAsync("baz") 617 }) 618 require.NoError(t, d.ExecuteUntilAllBlocked()) 619 require.True(t, d.IsDone()) 620 } 621 622 func TestDispatchClose(t *testing.T) { 623 var history []string 624 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 625 c := NewNamedChannel(ctx, "forever_blocked") 626 for i := 0; i < 10; i++ { 627 ii := i 628 GoNamed(ctx, fmt.Sprintf("c-%v", i), func(ctx Context) { 629 defer func() { 630 // Trigger no-longer-valid context access within deferred function. 631 // At this point deferred function should not continue and will be exited. 632 getState(ctx) 633 }() 634 c.Receive(ctx, nil) // blocked forever 635 history = append(history, fmt.Sprintf("child-%v", ii)) 636 }) 637 } 638 history = append(history, "root") 639 c.Receive(ctx, nil) // blocked forever 640 }) 641 require.EqualValues(t, 0, len(history)) 642 require.NoError(t, d.ExecuteUntilAllBlocked()) 643 require.False(t, d.IsDone()) 644 stack := d.StackTrace() 645 // 11 coroutines (3 lines each) + 10 nl 646 require.EqualValues(t, 11*3+10, len(strings.Split(stack, "\n")), stack) 647 require.Contains(t, stack, "coroutine 1 [blocked on forever_blocked.Receive]:") 648 for i := 0; i < 10; i++ { 649 require.Contains(t, stack, fmt.Sprintf("coroutine c-%v [blocked on forever_blocked.Receive]:", i)) 650 } 651 beforeClose := runtime.NumGoroutine() 652 d.Close() 653 time.Sleep(100 * time.Millisecond) // Let all goroutines to die 654 closedCount := beforeClose - runtime.NumGoroutine() 655 require.True(t, closedCount >= 11) 656 expected := []string{ 657 "root", 658 } 659 require.EqualValues(t, expected, history) 660 for _, c := range d.coroutines { 661 // Ensure that coroutines did not panic during dispatcher closing procedure 662 require.Nil(t, c.panicError) 663 } 664 } 665 666 func TestPanic(t *testing.T) { 667 var history []string 668 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 669 c := NewNamedChannel(ctx, "forever_blocked") 670 for i := 0; i < 10; i++ { 671 ii := i 672 GoNamed(ctx, fmt.Sprintf("c-%v", i), func(ctx Context) { 673 if ii == 9 { 674 panic("simulated failure") 675 } 676 c.Receive(ctx, nil) // blocked forever 677 history = append(history, fmt.Sprintf("child-%v", ii)) 678 }) 679 } 680 history = append(history, "root") 681 c.Receive(ctx, nil) // blocked forever 682 }) 683 require.EqualValues(t, 0, len(history)) 684 err := d.ExecuteUntilAllBlocked() 685 require.Error(t, err) 686 value := err.Error() 687 require.EqualValues(t, "simulated failure", value) 688 require.EqualValues(t, "simulated failure", err.Error()) 689 panicError, ok := err.(*workflowPanicError) 690 require.True(t, ok) 691 require.Contains(t, panicError.StackTrace(), "cadence/internal.TestPanic") 692 } 693 694 func TestAwait(t *testing.T) { 695 flag := false 696 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 697 Await(ctx, func() bool { return flag }) 698 }) 699 err := d.ExecuteUntilAllBlocked() 700 require.NoError(t, err) 701 require.False(t, d.IsDone()) 702 err = d.ExecuteUntilAllBlocked() 703 require.NoError(t, err) 704 require.False(t, d.IsDone()) 705 flag = true 706 err = d.ExecuteUntilAllBlocked() 707 require.NoError(t, err) 708 require.True(t, d.IsDone()) 709 } 710 711 func TestAwaitCancellation(t *testing.T) { 712 var awaitError error 713 ctx := createRootTestContext(t) 714 ctx, cancelHandler := WithCancel(ctx) 715 d, _ := newDispatcher(ctx, func(ctx Context) { 716 awaitError = Await(ctx, func() bool { return false }) 717 }) 718 err := d.ExecuteUntilAllBlocked() 719 require.NoError(t, err) 720 require.False(t, d.IsDone()) 721 cancelHandler() 722 err = d.ExecuteUntilAllBlocked() 723 require.NoError(t, err) 724 require.True(t, d.IsDone()) 725 require.Error(t, awaitError) 726 _, ok := awaitError.(*CanceledError) 727 require.True(t, ok) 728 } 729 730 func TestFutureSetValue(t *testing.T) { 731 var history []string 732 var f Future 733 var s Settable 734 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 735 f, s = NewFuture(ctx) 736 Go(ctx, func(ctx Context) { 737 history = append(history, "child-start") 738 require.False(t, f.IsReady()) 739 var v string 740 err := f.Get(ctx, &v) 741 require.NoError(t, err) 742 require.True(t, f.IsReady()) 743 history = append(history, fmt.Sprintf("future-get-%v", v)) 744 // test second get of the ready future 745 err = f.Get(ctx, &v) 746 require.NoError(t, err) 747 require.True(t, f.IsReady()) 748 history = append(history, fmt.Sprintf("child-end-%v", v)) 749 }) 750 history = append(history, "root-end") 751 752 }) 753 require.EqualValues(t, 0, len(history)) 754 require.NoError(t, d.ExecuteUntilAllBlocked()) 755 require.False(t, d.IsDone(), fmt.Sprintf("%v", d.StackTrace())) 756 history = append(history, "future-set") 757 require.False(t, f.IsReady()) 758 s.SetValue("value1") 759 require.True(t, f.IsReady()) 760 require.NoError(t, d.ExecuteUntilAllBlocked()) 761 require.True(t, d.IsDone()) 762 763 expected := []string{ 764 "root-end", 765 "child-start", 766 "future-set", 767 "future-get-value1", 768 "child-end-value1", 769 } 770 require.EqualValues(t, expected, history) 771 772 } 773 774 func TestFutureFail(t *testing.T) { 775 var history []string 776 var f Future 777 var s Settable 778 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 779 f, s = NewFuture(ctx) 780 Go(ctx, func(ctx Context) { 781 history = append(history, "child-start") 782 require.False(t, f.IsReady()) 783 var v string 784 err := f.Get(ctx, &v) 785 require.Error(t, err) 786 require.True(t, f.IsReady()) 787 history = append(history, fmt.Sprintf("future-get-%v", err)) 788 // test second get of the ready future 789 err = f.Get(ctx, &v) 790 require.Error(t, err) 791 require.True(t, f.IsReady()) 792 history = append(history, fmt.Sprintf("child-end-%v", err)) 793 }) 794 history = append(history, "root-end") 795 796 }) 797 require.EqualValues(t, 0, len(history)) 798 require.NoError(t, d.ExecuteUntilAllBlocked()) 799 require.False(t, d.IsDone(), fmt.Sprintf("%v", d.StackTrace())) 800 history = append(history, "future-set") 801 require.False(t, f.IsReady()) 802 s.SetError(errors.New("value1")) 803 assert.True(t, f.IsReady()) 804 require.NoError(t, d.ExecuteUntilAllBlocked()) 805 require.True(t, d.IsDone()) 806 807 expected := []string{ 808 "root-end", 809 "child-start", 810 "future-set", 811 "future-get-value1", 812 "child-end-value1", 813 } 814 require.EqualValues(t, expected, history) 815 } 816 817 func TestFutureSet(t *testing.T) { 818 var history []string 819 var f1, f2 Future 820 var s1, s2 Settable 821 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 822 f1, s1 = NewFuture(ctx) 823 f2, s2 = NewFuture(ctx) 824 Go(ctx, func(ctx Context) { 825 history = append(history, "child-start") 826 require.False(t, f1.IsReady()) 827 var v string 828 err := f1.Get(ctx, &v) 829 require.Error(t, err) 830 require.NotNil(t, v) 831 require.True(t, f1.IsReady()) 832 history = append(history, fmt.Sprintf("f1-get-%v-%v", v, err)) 833 // test second get of the ready future 834 err = f1.Get(ctx, &v) 835 require.Error(t, err) 836 require.True(t, f1.IsReady()) 837 history = append(history, fmt.Sprintf("f1-get2-%v-%v", v, err)) 838 839 err = f2.Get(ctx, &v) 840 require.NoError(t, err) 841 require.True(t, f2.IsReady()) 842 history = append(history, fmt.Sprintf("f2-get-%v-%v", v, err)) 843 844 // test second get of the ready future 845 err = f2.Get(ctx, &v) 846 require.NoError(t, err) 847 require.True(t, f1.IsReady()) 848 history = append(history, fmt.Sprintf("f2-get2-%v-%v", v, err)) 849 850 history = append(history, fmt.Sprintf("child-end")) 851 }) 852 history = append(history, "root-end") 853 }) 854 855 require.EqualValues(t, 0, len(history)) 856 require.NoError(t, d.ExecuteUntilAllBlocked()) 857 require.False(t, d.IsDone(), fmt.Sprintf("%v", d.StackTrace())) 858 history = append(history, "f1-set") 859 require.False(t, f1.IsReady()) 860 s1.Set("value-will-be-ignored", errors.New("error1")) 861 require.True(t, f1.IsReady()) 862 require.NoError(t, d.ExecuteUntilAllBlocked()) 863 864 require.False(t, d.IsDone(), fmt.Sprintf("%v", d.StackTrace())) 865 history = append(history, "f2-set") 866 require.False(t, f2.IsReady()) 867 s2.Set("value2", nil) 868 require.True(t, f2.IsReady()) 869 require.NoError(t, d.ExecuteUntilAllBlocked()) 870 require.True(t, d.IsDone()) 871 872 expected := []string{ 873 "root-end", 874 "child-start", 875 "f1-set", 876 "f1-get--error1", 877 "f1-get2--error1", 878 "f2-set", 879 "f2-get-value2-<nil>", 880 "f2-get2-value2-<nil>", 881 "child-end", 882 } 883 require.EqualValues(t, expected, history) 884 } 885 886 func TestFutureChain(t *testing.T) { 887 var history []string 888 var f1, cf1, f2, cf2 Future 889 var s1, cs1, s2, cs2 Settable 890 891 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 892 f1, s1 = NewFuture(ctx) 893 cf1, cs1 = NewFuture(ctx) 894 s1.Chain(cf1) 895 f2, s2 = NewFuture(ctx) 896 cf2, cs2 = NewFuture(ctx) 897 s2.Chain(cf2) 898 Go(ctx, func(ctx Context) { 899 history = append(history, "child-start") 900 require.False(t, f1.IsReady()) 901 var v string 902 err := f1.Get(ctx, &v) 903 require.Error(t, err) 904 require.True(t, f1.IsReady()) 905 history = append(history, fmt.Sprintf("f1-get-%v-%v", v, err)) 906 // test second get of the ready future 907 err = f1.Get(ctx, &v) 908 require.Error(t, err) 909 require.True(t, f1.IsReady()) 910 history = append(history, fmt.Sprintf("f1-get2-%v-%v", v, err)) 911 912 err = f2.Get(ctx, &v) 913 require.NoError(t, err) 914 require.Equal(t, "value2", v) 915 require.True(t, f2.IsReady()) 916 history = append(history, fmt.Sprintf("f2-get-%v-%v", v, err)) 917 // test second get of the ready future 918 err = f2.Get(ctx, &v) 919 require.NoError(t, err) 920 require.Equal(t, "value2", v) 921 require.True(t, f2.IsReady()) 922 history = append(history, fmt.Sprintf("f2-get2-%v-%v", v, err)) 923 }) 924 history = append(history, "root-end") 925 926 }) 927 require.EqualValues(t, 0, len(history)) 928 require.NoError(t, d.ExecuteUntilAllBlocked()) 929 require.False(t, d.IsDone(), fmt.Sprintf("%v", d.StackTrace())) 930 history = append(history, "f1-set") 931 require.False(t, f1.IsReady()) 932 cs1.Set("value1-will-be-ignored", errors.New("error1")) 933 require.True(t, f1.IsReady()) 934 require.NoError(t, d.ExecuteUntilAllBlocked()) 935 936 require.False(t, d.IsDone(), fmt.Sprintf("%v", d.StackTrace())) 937 history = append(history, "f2-set") 938 require.False(t, f2.IsReady()) 939 cs2.Set("value2", nil) 940 require.True(t, f2.IsReady()) 941 require.NoError(t, d.ExecuteUntilAllBlocked()) 942 943 require.True(t, d.IsDone()) 944 945 expected := []string{ 946 "root-end", 947 "child-start", 948 "f1-set", 949 "f1-get--error1", 950 "f1-get2--error1", 951 "f2-set", 952 "f2-get-value2-<nil>", 953 "f2-get2-value2-<nil>", 954 } 955 require.EqualValues(t, expected, history) 956 } 957 958 func TestSelectFuture(t *testing.T) { 959 var history []string 960 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 961 future1, settable1 := NewFuture(ctx) 962 future2, settable2 := NewFuture(ctx) 963 Go(ctx, func(ctx Context) { 964 history = append(history, "add-one") 965 settable1.SetValue("one") 966 }) 967 Go(ctx, func(ctx Context) { 968 history = append(history, "add-two") 969 settable2.SetValue("two") 970 }) 971 972 s := NewSelector(ctx) 973 s. 974 AddFuture(future1, func(f Future) { 975 var v string 976 err := f.Get(ctx, &v) 977 require.NoError(t, err) 978 history = append(history, fmt.Sprintf("c1-%v", v)) 979 }). 980 AddFuture(future2, func(f Future) { 981 var v string 982 err := f.Get(ctx, &v) 983 require.NoError(t, err) 984 history = append(history, fmt.Sprintf("c2-%v", v)) 985 }) 986 history = append(history, "select1") 987 s.Select(ctx) 988 history = append(history, "select2") 989 s.Select(ctx) 990 history = append(history, "done") 991 }) 992 require.NoError(t, d.ExecuteUntilAllBlocked()) 993 require.True(t, d.IsDone()) 994 995 expected := []string{ 996 "select1", 997 "add-one", 998 "add-two", 999 "c1-one", 1000 "select2", 1001 "c2-two", 1002 "done", 1003 } 1004 require.EqualValues(t, expected, history) 1005 } 1006 1007 func TestSelectDecodeFuture(t *testing.T) { 1008 var history []string 1009 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 1010 future1, settable1 := newDecodeFuture(ctx, "testFn1") 1011 future2, settable2 := newDecodeFuture(ctx, "testFn2") 1012 Go(ctx, func(ctx Context) { 1013 history = append(history, "add-one") 1014 settable1.SetValue([]byte("one")) 1015 }) 1016 Go(ctx, func(ctx Context) { 1017 history = append(history, "add-two") 1018 settable2.SetValue([]byte("two")) 1019 }) 1020 1021 s := NewSelector(ctx) 1022 s. 1023 AddFuture(future1, func(f Future) { 1024 var v []byte 1025 err := f.Get(ctx, &v) 1026 require.NoError(t, err) 1027 history = append(history, fmt.Sprintf("c1-%s", v)) 1028 }). 1029 AddFuture(future2, func(f Future) { 1030 var v []byte 1031 err := f.Get(ctx, &v) 1032 require.NoError(t, err) 1033 history = append(history, fmt.Sprintf("c2-%s", v)) 1034 }) 1035 history = append(history, "select1") 1036 s.Select(ctx) 1037 history = append(history, "select2") 1038 s.Select(ctx) 1039 history = append(history, "done") 1040 }) 1041 require.NoError(t, d.ExecuteUntilAllBlocked()) 1042 require.True(t, d.IsDone()) 1043 1044 expected := []string{ 1045 "select1", 1046 "add-one", 1047 "add-two", 1048 "c1-one", 1049 "select2", 1050 "c2-two", 1051 "done", 1052 } 1053 require.EqualValues(t, expected, history) 1054 } 1055 1056 func TestDecodeFutureChain(t *testing.T) { 1057 var history []string 1058 var f1, cf1, f2, cf2 Future 1059 var s1, cs1, s2, cs2 Settable 1060 1061 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 1062 f1, s1 = newDecodeFuture(ctx, "testFn") 1063 cf1, cs1 = newDecodeFuture(ctx, "testFun") 1064 f2, s2 = newDecodeFuture(ctx, "testFn") 1065 cf2, cs2 = newDecodeFuture(ctx, "testFun") 1066 s1.Chain(cf1) 1067 s2.Chain(cf2) 1068 Go(ctx, func(ctx Context) { 1069 history = append(history, "child-start") 1070 require.False(t, f1.IsReady()) 1071 var v []byte 1072 err := f1.Get(ctx, &v) 1073 require.Error(t, err) 1074 require.Nil(t, v) 1075 require.True(t, f1.IsReady()) 1076 history = append(history, fmt.Sprintf("f1-get-%s-%v", v, err)) 1077 // test second get of the ready future 1078 err = f1.Get(ctx, &v) 1079 require.Error(t, err) 1080 require.Nil(t, v) 1081 require.True(t, f1.IsReady()) 1082 history = append(history, fmt.Sprintf("f1-get2-%s-%v", v, err)) 1083 1084 // for f2 1085 err = f2.Get(ctx, &v) 1086 require.NoError(t, err) 1087 require.NotNil(t, v) 1088 require.True(t, f1.IsReady()) 1089 history = append(history, fmt.Sprintf("f2-get-%s-%v", v, err)) 1090 // test second get of the ready future 1091 err = f2.Get(ctx, &v) 1092 require.NoError(t, err) 1093 require.NotNil(t, v) 1094 require.True(t, f2.IsReady()) 1095 history = append(history, fmt.Sprintf("f2-get2-%s-%v", v, err)) 1096 }) 1097 history = append(history, "root-end") 1098 }) 1099 require.EqualValues(t, 0, len(history)) 1100 require.NoError(t, d.ExecuteUntilAllBlocked()) 1101 // set f1 1102 require.False(t, d.IsDone(), fmt.Sprintf("%v", d.StackTrace())) 1103 history = append(history, "f1-set") 1104 require.False(t, f1.IsReady()) 1105 cs1.Set([]byte("value-will-be-ignored"), errors.New("error1")) 1106 require.True(t, f1.IsReady()) 1107 require.NoError(t, d.ExecuteUntilAllBlocked()) 1108 1109 // set f2 1110 require.False(t, d.IsDone(), fmt.Sprintf("%v", d.StackTrace())) 1111 history = append(history, "f2-set") 1112 require.False(t, f2.IsReady()) 1113 cs2.Set([]byte("value2"), nil) 1114 require.True(t, f2.IsReady()) 1115 require.NoError(t, d.ExecuteUntilAllBlocked()) 1116 1117 require.True(t, d.IsDone()) 1118 1119 expected := []string{ 1120 "root-end", 1121 "child-start", 1122 "f1-set", 1123 "f1-get--error1", 1124 "f1-get2--error1", 1125 "f2-set", 1126 "f2-get-value2-<nil>", 1127 "f2-get2-value2-<nil>", 1128 } 1129 require.EqualValues(t, expected, history) 1130 } 1131 1132 func TestSelectFuture_WithBatchSets(t *testing.T) { 1133 var history []string 1134 d, _ := newDispatcher(createRootTestContext(t), func(ctx Context) { 1135 future1, settable1 := NewFuture(ctx) 1136 future2, settable2 := NewFuture(ctx) 1137 future3, settable3 := NewFuture(ctx) 1138 1139 s := NewSelector(ctx) 1140 s. 1141 AddFuture(future1, func(f Future) { 1142 var v string 1143 err := f.Get(ctx, &v) 1144 require.NoError(t, err) 1145 history = append(history, fmt.Sprintf("c1-%v", v)) 1146 }). 1147 AddFuture(future2, func(f Future) { 1148 var v string 1149 err := f.Get(ctx, &v) 1150 require.NoError(t, err) 1151 history = append(history, fmt.Sprintf("c2-%v", v)) 1152 }). 1153 AddFuture(future3, func(f Future) { 1154 var v string 1155 err := f.Get(ctx, &v) 1156 require.NoError(t, err) 1157 history = append(history, fmt.Sprintf("c3-%v", v)) 1158 }) 1159 1160 settable2.Set("two", nil) 1161 s.Select(ctx) 1162 settable3.Set("three", nil) 1163 settable1.Set("one", nil) 1164 s.Select(ctx) 1165 s.Select(ctx) 1166 }) 1167 require.NoError(t, d.ExecuteUntilAllBlocked()) 1168 require.True(t, d.IsDone()) 1169 1170 expected := []string{ 1171 "c2-two", 1172 "c1-one", 1173 "c3-three", 1174 } 1175 require.EqualValues(t, expected, history) 1176 } 1177 1178 func TestChainedFuture(t *testing.T) { 1179 activityFn := func(arg int) (int, error) { 1180 return arg, nil 1181 } 1182 workflowFn := func(ctx Context) (int, error) { 1183 ctx = WithActivityOptions(ctx, ActivityOptions{ 1184 ScheduleToStartTimeout: time.Minute, 1185 StartToCloseTimeout: time.Minute, 1186 }) 1187 f := ExecuteActivity(ctx, activityFn, 5) 1188 var out int 1189 fut, set := NewFuture(ctx) 1190 set.Chain(f) 1191 require.NoError(t, fut.Get(ctx, &out)) 1192 return out, nil 1193 } 1194 1195 env := newTestWorkflowEnv(t) 1196 env.RegisterWorkflow(workflowFn) 1197 env.RegisterActivity(activityFn) 1198 1199 env.ExecuteWorkflow(workflowFn) 1200 err := env.GetWorkflowError() 1201 require.NoError(t, err) 1202 var out int 1203 require.NoError(t, env.GetWorkflowResult(&out)) 1204 require.Equal(t, 5, out) 1205 }