skipped 28 lines 29 29 "github.com/mum4k/termdash/internal/event" 30 30 "github.com/mum4k/termdash/internal/event/testevent" 31 31 "github.com/mum4k/termdash/internal/faketerm" 32 - "github.com/mum4k/termdash/internal/widgetapi " 32 + "github.com/mum4k/termdash/internal/fakewidget " 33 33 "github.com/mum4k/termdash/keyboard" 34 34 "github.com/mum4k/termdash/linestyle" 35 35 "github.com/mum4k/termdash/mouse" 36 36 "github.com/mum4k/termdash/terminal/terminalapi" 37 + "github.com/mum4k/termdash/widgetapi" 37 38 "github.com/mum4k/termdash/widgets/barchart" 38 - "github.com/mum4k/termdash/widgets/fakewidget" 39 39 ) 40 40 41 41 // Example demonstrates how to use the Container API. skipped 42 lines 84 84 want func(size image.Point) *faketerm.Terminal 85 85 }{ 86 86 { 87 + desc: "fails on MarginTop too low", 88 + termSize: image.Point{10, 10}, 89 + container: func(ft *faketerm.Terminal) (*Container, error) { 90 + return New(ft, MarginTop(-1)) 91 + }, 92 + wantContainerErr: true, 93 + }, 94 + { 95 + desc: "fails on invalid option on the first vertical child container", 96 + termSize: image.Point{10, 10}, 97 + container: func(ft *faketerm.Terminal) (*Container, error) { 98 + return New( 99 + ft, 100 + SplitVertical( 101 + Left( 102 + MarginTop(-1), 103 + ), 104 + Right(), 105 + ), 106 + ) 107 + }, 108 + wantContainerErr: true, 109 + }, 110 + { 111 + desc: "fails on invalid option on the second vertical child container", 112 + termSize: image.Point{10, 10}, 113 + container: func(ft *faketerm.Terminal) (*Container, error) { 114 + return New( 115 + ft, 116 + SplitVertical( 117 + Left(), 118 + Right( 119 + MarginTop(-1), 120 + ), 121 + ), 122 + ) 123 + }, 124 + wantContainerErr: true, 125 + }, 126 + { 127 + desc: "fails on invalid option on the first horizontal child container", 128 + termSize: image.Point{10, 10}, 129 + container: func(ft *faketerm.Terminal) (*Container, error) { 130 + return New( 131 + ft, 132 + SplitHorizontal( 133 + Top( 134 + MarginTop(-1), 135 + ), 136 + Bottom(), 137 + ), 138 + ) 139 + }, 140 + wantContainerErr: true, 141 + }, 142 + { 143 + desc: "fails on invalid option on the second horizontal child container", 144 + termSize: image.Point{10, 10}, 145 + container: func(ft *faketerm.Terminal) (*Container, error) { 146 + return New( 147 + ft, 148 + SplitHorizontal( 149 + Top(), 150 + Bottom( 151 + MarginTop(-1), 152 + ), 153 + ), 154 + ) 155 + }, 156 + wantContainerErr: true, 157 + }, 158 + { 159 + desc: "fails on MarginTopPercent too low", 160 + termSize: image.Point{10, 10}, 161 + container: func(ft *faketerm.Terminal) (*Container, error) { 162 + return New(ft, MarginTopPercent(-1)) 163 + }, 164 + wantContainerErr: true, 165 + }, 166 + { 167 + desc: "fails on MarginTopPercent too high", 168 + termSize: image.Point{10, 10}, 169 + container: func(ft *faketerm.Terminal) (*Container, error) { 170 + return New(ft, MarginTopPercent(101)) 171 + }, 172 + wantContainerErr: true, 173 + }, 174 + { 175 + desc: "fails when both MarginTop and MarginTopPercent specified", 176 + termSize: image.Point{10, 10}, 177 + container: func(ft *faketerm.Terminal) (*Container, error) { 178 + return New(ft, MarginTop(1), MarginTopPercent(1)) 179 + }, 180 + wantContainerErr: true, 181 + }, 182 + { 183 + desc: "fails when both MarginTopPercent and MarginTop specified", 184 + termSize: image.Point{10, 10}, 185 + container: func(ft *faketerm.Terminal) (*Container, error) { 186 + return New(ft, MarginTopPercent(1), MarginTop(1)) 187 + }, 188 + wantContainerErr: true, 189 + }, 190 + { 191 + desc: "fails on MarginRight too low", 192 + termSize: image.Point{10, 10}, 193 + container: func(ft *faketerm.Terminal) (*Container, error) { 194 + return New(ft, MarginRight(-1)) 195 + }, 196 + wantContainerErr: true, 197 + }, 198 + { 199 + desc: "fails on MarginRightPercent too low", 200 + termSize: image.Point{10, 10}, 201 + container: func(ft *faketerm.Terminal) (*Container, error) { 202 + return New(ft, MarginRightPercent(-1)) 203 + }, 204 + wantContainerErr: true, 205 + }, 206 + { 207 + desc: "fails on MarginRightPercent too high", 208 + termSize: image.Point{10, 10}, 209 + container: func(ft *faketerm.Terminal) (*Container, error) { 210 + return New(ft, MarginRightPercent(101)) 211 + }, 212 + wantContainerErr: true, 213 + }, 214 + { 215 + desc: "fails when both MarginRight and MarginRightPercent specified", 216 + termSize: image.Point{10, 10}, 217 + container: func(ft *faketerm.Terminal) (*Container, error) { 218 + return New(ft, MarginRight(1), MarginRightPercent(1)) 219 + }, 220 + wantContainerErr: true, 221 + }, 222 + { 223 + desc: "fails when both MarginRightPercent and MarginRight specified", 224 + termSize: image.Point{10, 10}, 225 + container: func(ft *faketerm.Terminal) (*Container, error) { 226 + return New(ft, MarginRightPercent(1), MarginRight(1)) 227 + }, 228 + wantContainerErr: true, 229 + }, 230 + { 231 + desc: "fails on MarginBottom too low", 232 + termSize: image.Point{10, 10}, 233 + container: func(ft *faketerm.Terminal) (*Container, error) { 234 + return New(ft, MarginBottom(-1)) 235 + }, 236 + wantContainerErr: true, 237 + }, 238 + { 239 + desc: "fails on MarginBottomPercent too low", 240 + termSize: image.Point{10, 10}, 241 + container: func(ft *faketerm.Terminal) (*Container, error) { 242 + return New(ft, MarginBottomPercent(-1)) 243 + }, 244 + wantContainerErr: true, 245 + }, 246 + { 247 + desc: "fails on MarginBottomPercent too high", 248 + termSize: image.Point{10, 10}, 249 + container: func(ft *faketerm.Terminal) (*Container, error) { 250 + return New(ft, MarginBottomPercent(101)) 251 + }, 252 + wantContainerErr: true, 253 + }, 254 + { 255 + desc: "fails when both MarginBottom and MarginBottomPercent specified", 256 + termSize: image.Point{10, 10}, 257 + container: func(ft *faketerm.Terminal) (*Container, error) { 258 + return New(ft, MarginBottom(1), MarginBottomPercent(1)) 259 + }, 260 + wantContainerErr: true, 261 + }, 262 + { 263 + desc: "fails when both MarginBottomPercent and MarginBottom specified", 264 + termSize: image.Point{10, 10}, 265 + container: func(ft *faketerm.Terminal) (*Container, error) { 266 + return New(ft, MarginBottomPercent(1), MarginBottom(1)) 267 + }, 268 + wantContainerErr: true, 269 + }, 270 + { 271 + desc: "fails on MarginLeft too low", 272 + termSize: image.Point{10, 10}, 273 + container: func(ft *faketerm.Terminal) (*Container, error) { 274 + return New(ft, MarginLeft(-1)) 275 + }, 276 + wantContainerErr: true, 277 + }, 278 + { 279 + desc: "fails on MarginLeftPercent too low", 280 + termSize: image.Point{10, 10}, 281 + container: func(ft *faketerm.Terminal) (*Container, error) { 282 + return New(ft, MarginLeftPercent(-1)) 283 + }, 284 + wantContainerErr: true, 285 + }, 286 + { 287 + desc: "fails on MarginLeftPercent too high", 288 + termSize: image.Point{10, 10}, 289 + container: func(ft *faketerm.Terminal) (*Container, error) { 290 + return New(ft, MarginLeftPercent(101)) 291 + }, 292 + wantContainerErr: true, 293 + }, 294 + { 295 + desc: "fails when both MarginLeft and MarginLeftPercent specified", 296 + termSize: image.Point{10, 10}, 297 + container: func(ft *faketerm.Terminal) (*Container, error) { 298 + return New(ft, MarginLeft(1), MarginLeftPercent(1)) 299 + }, 300 + wantContainerErr: true, 301 + }, 302 + { 303 + desc: "fails when both MarginLeftPercent and MarginLeft specified", 304 + termSize: image.Point{10, 10}, 305 + container: func(ft *faketerm.Terminal) (*Container, error) { 306 + return New(ft, MarginLeftPercent(1), MarginLeft(1)) 307 + }, 308 + wantContainerErr: true, 309 + }, 310 + { 311 + desc: "fails on PaddingTop too low", 312 + termSize: image.Point{10, 10}, 313 + container: func(ft *faketerm.Terminal) (*Container, error) { 314 + return New(ft, PaddingTop(-1)) 315 + }, 316 + wantContainerErr: true, 317 + }, 318 + { 319 + desc: "fails on PaddingTopPercent too low", 320 + termSize: image.Point{10, 10}, 321 + container: func(ft *faketerm.Terminal) (*Container, error) { 322 + return New(ft, PaddingTopPercent(-1)) 323 + }, 324 + wantContainerErr: true, 325 + }, 326 + { 327 + desc: "fails on PaddingTopPercent too high", 328 + termSize: image.Point{10, 10}, 329 + container: func(ft *faketerm.Terminal) (*Container, error) { 330 + return New(ft, PaddingTopPercent(101)) 331 + }, 332 + wantContainerErr: true, 333 + }, 334 + { 335 + desc: "fails when both PaddingTop and PaddingTopPercent specified", 336 + termSize: image.Point{10, 10}, 337 + container: func(ft *faketerm.Terminal) (*Container, error) { 338 + return New(ft, PaddingTop(1), PaddingTopPercent(1)) 339 + }, 340 + wantContainerErr: true, 341 + }, 342 + { 343 + desc: "fails when both PaddingTopPercent and PaddingTop specified", 344 + termSize: image.Point{10, 10}, 345 + container: func(ft *faketerm.Terminal) (*Container, error) { 346 + return New(ft, PaddingTopPercent(1), PaddingTop(1)) 347 + }, 348 + wantContainerErr: true, 349 + }, 350 + { 351 + desc: "fails on PaddingRight too low", 352 + termSize: image.Point{10, 10}, 353 + container: func(ft *faketerm.Terminal) (*Container, error) { 354 + return New(ft, PaddingRight(-1)) 355 + }, 356 + wantContainerErr: true, 357 + }, 358 + { 359 + desc: "fails on PaddingRightPercent too low", 360 + termSize: image.Point{10, 10}, 361 + container: func(ft *faketerm.Terminal) (*Container, error) { 362 + return New(ft, PaddingRightPercent(-1)) 363 + }, 364 + wantContainerErr: true, 365 + }, 366 + { 367 + desc: "fails on PaddingRightPercent too high", 368 + termSize: image.Point{10, 10}, 369 + container: func(ft *faketerm.Terminal) (*Container, error) { 370 + return New(ft, PaddingRightPercent(101)) 371 + }, 372 + wantContainerErr: true, 373 + }, 374 + { 375 + desc: "fails when both PaddingRight and PaddingRightPercent specified", 376 + termSize: image.Point{10, 10}, 377 + container: func(ft *faketerm.Terminal) (*Container, error) { 378 + return New(ft, PaddingRight(1), PaddingRightPercent(1)) 379 + }, 380 + wantContainerErr: true, 381 + }, 382 + { 383 + desc: "fails when both PaddingRightPercent and PaddingRight specified", 384 + termSize: image.Point{10, 10}, 385 + container: func(ft *faketerm.Terminal) (*Container, error) { 386 + return New(ft, PaddingRightPercent(1), PaddingRight(1)) 387 + }, 388 + wantContainerErr: true, 389 + }, 390 + { 391 + desc: "fails on PaddingBottom too low", 392 + termSize: image.Point{10, 10}, 393 + container: func(ft *faketerm.Terminal) (*Container, error) { 394 + return New(ft, PaddingBottom(-1)) 395 + }, 396 + wantContainerErr: true, 397 + }, 398 + { 399 + desc: "fails on PaddingBottomPercent too low", 400 + termSize: image.Point{10, 10}, 401 + container: func(ft *faketerm.Terminal) (*Container, error) { 402 + return New(ft, PaddingBottomPercent(-1)) 403 + }, 404 + wantContainerErr: true, 405 + }, 406 + { 407 + desc: "fails on PaddingBottomPercent too high", 408 + termSize: image.Point{10, 10}, 409 + container: func(ft *faketerm.Terminal) (*Container, error) { 410 + return New(ft, PaddingBottomPercent(101)) 411 + }, 412 + wantContainerErr: true, 413 + }, 414 + { 415 + desc: "fails when both PaddingBottom and PaddingBottomPercent specified", 416 + termSize: image.Point{10, 10}, 417 + container: func(ft *faketerm.Terminal) (*Container, error) { 418 + return New(ft, PaddingBottom(1), PaddingBottomPercent(1)) 419 + }, 420 + wantContainerErr: true, 421 + }, 422 + { 423 + desc: "fails when both PaddingBottomPercent and PaddingBottom specified", 424 + termSize: image.Point{10, 10}, 425 + container: func(ft *faketerm.Terminal) (*Container, error) { 426 + return New(ft, PaddingBottomPercent(1), PaddingBottom(1)) 427 + }, 428 + wantContainerErr: true, 429 + }, 430 + { 431 + desc: "fails on PaddingLeft too low", 432 + termSize: image.Point{10, 10}, 433 + container: func(ft *faketerm.Terminal) (*Container, error) { 434 + return New(ft, PaddingLeft(-1)) 435 + }, 436 + wantContainerErr: true, 437 + }, 438 + { 439 + desc: "fails on PaddingLeftPercent too low", 440 + termSize: image.Point{10, 10}, 441 + container: func(ft *faketerm.Terminal) (*Container, error) { 442 + return New(ft, PaddingLeftPercent(-1)) 443 + }, 444 + wantContainerErr: true, 445 + }, 446 + { 447 + desc: "fails on PaddingLeftPercent too high", 448 + termSize: image.Point{10, 10}, 449 + container: func(ft *faketerm.Terminal) (*Container, error) { 450 + return New(ft, PaddingLeftPercent(101)) 451 + }, 452 + wantContainerErr: true, 453 + }, 454 + { 455 + desc: "fails when both PaddingLeft and PaddingLeftPercent specified", 456 + termSize: image.Point{10, 10}, 457 + container: func(ft *faketerm.Terminal) (*Container, error) { 458 + return New(ft, PaddingLeft(1), PaddingLeftPercent(1)) 459 + }, 460 + wantContainerErr: true, 461 + }, 462 + { 463 + desc: "fails when both PaddingLeftPercent and PaddingLeft specified", 464 + termSize: image.Point{10, 10}, 465 + container: func(ft *faketerm.Terminal) (*Container, error) { 466 + return New(ft, PaddingLeftPercent(1), PaddingLeft(1)) 467 + }, 468 + wantContainerErr: true, 469 + }, 470 + { 471 + desc: "fails on empty ID specified", 472 + termSize: image.Point{10, 10}, 473 + container: func(ft *faketerm.Terminal) (*Container, error) { 474 + return New(ft, ID("")) 475 + }, 476 + wantContainerErr: true, 477 + }, 478 + { 479 + desc: "fails on empty duplicate ID specified", 480 + termSize: image.Point{10, 10}, 481 + container: func(ft *faketerm.Terminal) (*Container, error) { 482 + return New( 483 + ft, 484 + ID("0"), 485 + SplitHorizontal( 486 + Top(ID("1")), 487 + Bottom(ID("1")), 488 + ), 489 + ) 490 + }, 491 + wantContainerErr: true, 492 + }, 493 + { 87 494 desc: "empty container", 88 495 termSize: image.Point{10, 10}, 89 496 container: func(ft *faketerm.Terminal) (*Container, error) { skipped 432 lines 522 929 if err != nil { 523 930 return 524 931 } 932 + contStr := cont.String() 933 + t.Logf("For container: %v", contStr) 525 934 if err := cont.Draw(); err != nil { 526 935 t.Fatalf("Draw => unexpected error: %v", err) 527 936 } 528 937 529 - if diff := faketerm.Diff(tc.want(tc.termSize), got); diff != "" { 938 + var want *faketerm.Terminal 939 + if tc.want != nil { 940 + want = tc.want(tc.termSize) 941 + } else { 942 + w, err := faketerm.New(tc.termSize) 943 + if err != nil { 944 + t.Fatalf("faketerm.New => unexpected error: %v", err) 945 + } 946 + want = w 947 + } 948 + if diff := faketerm.Diff(want, got); diff != "" { 530 949 t.Errorf("Draw => %v", diff) 531 950 } 532 951 }) skipped 1 lines 534 953 535 954 } 536 955 537 - // eventGroup is a group of events to be delivered with synchronization. 538 - // I.e. the test execution waits until the specified number is processed before 539 - // proceeding with test execution. 540 - type eventGroup struct { 541 - events []terminalapi.Event 542 - wantProcessed int 543 - } 544 - 545 956 // errorHandler just stores the last error received. 546 957 type errorHandler struct { 547 958 err error skipped 14 lines 562 973 563 974 func TestKeyboard(t *testing.T) { 564 975 tests := []struct { 565 - desc string 566 - termSize image.Point 567 - container func(ft *faketerm.Terminal) (*Container, error) 568 - eventGroups []*eventGroup 569 - want func(size image.Point) *faketerm.Terminal 570 - wantErr bool 976 + desc string 977 + termSize image.Point 978 + container func(ft *faketerm.Terminal) (*Container, error) 979 + events []terminalapi.Event 980 + // If specified, waits for this number of events. 981 + // Otherwise waits for len(events). 982 + wantProcessed int 983 + want func(size image.Point) *faketerm.Terminal 984 + wantErr bool 571 985 }{ 572 986 { 573 987 desc: "event not forwarded if container has no widget", skipped 1 lines 575 989 container: func(ft *faketerm.Terminal) (*Container, error) { 576 990 return New(ft) 577 991 }, 578 - eventGroups: []*eventGroup{ 579 - { 580 - events: []terminalapi.Event{ 581 - &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 582 - }, 583 - wantProcessed: 0, 584 - }, 992 + events: []terminalapi.Event{ 993 + &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 585 994 }, 586 995 want: func(size image.Point) *faketerm.Terminal { 587 996 return faketerm.MustNew(size) skipped 22 lines 610 1019 ), 611 1020 ) 612 1021 }, 613 - eventGroups: []*eventGroup{ 1022 + events: []terminalapi.Event{ 614 1023 // Move focus to the target container. 615 - { 616 - events: []terminalapi.Event{ 617 - &terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonLeft}, 618 - &terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonRelease}, 619 - }, 620 - wantProcessed: 2, 621 - }, 1024 + &terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonLeft}, 1025 + &terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonRelease}, 622 1026 // Send the keyboard event. 623 - { 624 - events: []terminalapi.Event{ 625 - &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 626 - }, 627 - wantProcessed: 5, 628 - }, 1027 + &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 629 1028 }, 630 - 631 1029 want: func(size image.Point) *faketerm.Terminal { 632 1030 ft := faketerm.MustNew(size) 633 1031 skipped 42 lines 676 1074 ), 677 1075 ) 678 1076 }, 679 - eventGroups: []*eventGroup{ 1077 + events: []terminalapi.Event{ 680 1078 // Move focus to the target container. 681 - { 682 - events: []terminalapi.Event{ 683 - &terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonLeft}, 684 - &terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonRelease}, 685 - }, 686 - wantProcessed: 2, 687 - }, 1079 + &terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonLeft}, 1080 + &terminalapi.Mouse{Position: image.Point{39, 19}, Button: mouse.ButtonRelease}, 688 1081 // Send the keyboard event. 689 - { 690 - events: []terminalapi.Event{ 691 - &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 692 - }, 693 - wantProcessed: 5, 694 - }, 1082 + &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 695 1083 }, 696 - 697 1084 want: func(size image.Point) *faketerm.Terminal { 698 1085 ft := faketerm.MustNew(size) 699 1086 skipped 32 lines 732 1119 PlaceWidget(fakewidget.New(widgetapi.Options{WantKeyboard: widgetapi.KeyScopeNone})), 733 1120 ) 734 1121 }, 735 - eventGroups: []*eventGroup{ 736 - { 737 - events: []terminalapi.Event{ 738 - &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 739 - }, 740 - wantProcessed: 0, 741 - }, 1122 + events: []terminalapi.Event{ 1123 + &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 742 1124 }, 743 1125 want: func(size image.Point) *faketerm.Terminal { 744 1126 ft := faketerm.MustNew(size) skipped 15 lines 760 1142 PlaceWidget(fakewidget.New(widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused})), 761 1143 ) 762 1144 }, 763 - eventGroups: []*eventGroup{ 764 - { 765 - events: []terminalapi.Event{ 766 - &terminalapi.Keyboard{Key: keyboard.KeyEsc}, 767 - }, 768 - wantProcessed: 2, 769 - }, 1145 + events: []terminalapi.Event{ 1146 + &terminalapi.Keyboard{Key: keyboard.KeyEsc}, 770 1147 }, 1148 + wantProcessed: 2, // The error is also an event. 771 1149 want: func(size image.Point) *faketerm.Terminal { 772 1150 ft := faketerm.MustNew(size) 773 1151 skipped 28 lines 802 1180 }) 803 1181 804 1182 c.Subscribe(eds) 805 - for _, eg := range tc.eventGroups { 806 - for _, ev := range eg.events { 807 - eds.Event(ev) 1183 + // Initial draw to determine sizes of containers. 1184 + if err := c.Draw(); err != nil { 1185 + t.Fatalf("Draw => unexpected error: %v", err) 1186 + } 1187 + for _, ev := range tc.events { 1188 + eds.Event(ev) 1189 + } 1190 + var wantEv int 1191 + if tc.wantProcessed != 0 { 1192 + wantEv = tc.wantProcessed 1193 + } else { 1194 + wantEv = len(tc.events) 1195 + } 1196 + 1197 + if err := testevent.WaitFor(5*time.Second, func() error { 1198 + if got, want := eds.Processed(), wantEv; got != want { 1199 + return fmt.Errorf("the event distribution system processed %d events, want %d", got, want) 808 1200 } 809 - if err := testevent.WaitFor(5*time.Second, func() error { 810 - if got, want := eds.Processed(), eg.wantProcessed; got != want { 811 - return fmt.Errorf("the event distribution system processed %d events, want %d", got, want) 812 - } 813 - return nil 814 - }); err != nil { 815 - t.Fatalf("testevent.WaitFor => %v", err) 816 - } 1201 + return nil 1202 + }); err != nil { 1203 + t.Fatalf("testevent.WaitFor => %v", err) 817 1204 } 818 1205 819 1206 if err := c.Draw(); err != nil { skipped 13 lines 833 1220 834 1221 func TestMouse(t *testing.T) { 835 1222 tests := []struct { 836 - desc string 837 - termSize image.Point 838 - container func(ft *faketerm.Terminal) (*Container, error) 839 - events []terminalapi.Event 1223 + desc string 1224 + termSize image.Point 1225 + container func(ft *faketerm.Terminal) (*Container, error) 1226 + events []terminalapi.Event 1227 + // If specified, waits for this number of events. 1228 + // Otherwise waits for len(events). 1229 + wantProcessed int 840 1230 want func(size image.Point) *faketerm.Terminal 841 - wantProcessed int 842 1231 wantErr bool 843 1232 }{ 844 1233 { skipped 19 lines 864 1253 ) 865 1254 return ft 866 1255 }, 867 - wantProcessed: 4, 868 1256 }, 869 1257 { 870 1258 desc: "event not forwarded if container has no widget", skipped 8 lines 879 1267 want: func(size image.Point) *faketerm.Terminal { 880 1268 return faketerm.MustNew(size) 881 1269 }, 882 - wantProcessed: 2, 883 1270 }, 884 1271 { 885 1272 desc: "event forwarded to container at that point", skipped 24 lines 910 1297 }, 911 1298 want: func(size image.Point) *faketerm.Terminal { 912 1299 ft := faketerm.MustNew(size) 913 - // Widgets that aren't focused don't get the mouse clicks. 1300 + // Widgets that aren't targeted don't get the mouse clicks. 914 1301 fakewidget.MustDraw( 915 1302 ft, 916 1303 testcanvas.MustNew(image.Rect(0, 0, 25, 20)), skipped 6 lines 923 1310 &terminalapi.Keyboard{}, 924 1311 ) 925 1312 926 - // The focused widget receives the key . 1313 + // The target widget receives the mouse event . 927 1314 fakewidget.MustDraw( 928 1315 ft, 929 1316 testcanvas.MustNew(image.Rect(25, 0, 50, 10)), skipped 3 lines 933 1320 ) 934 1321 return ft 935 1322 }, 936 - wantProcessed: 8, 1323 + }, 1324 + { 1325 + desc: "event focuses the target container after terminal resize (falls onto the new area), regression for #169", 1326 + termSize: image.Point{50, 20}, 1327 + container: func(ft *faketerm.Terminal) (*Container, error) { 1328 + // Decrease the terminal size, so when container is created, it 1329 + // only sees width of 30. 1330 + if err := ft.Resize(image.Point{30, 20}); err != nil { 1331 + return nil, err 1332 + } 1333 + c, err := New( 1334 + ft, 1335 + SplitVertical( 1336 + Left( 1337 + PlaceWidget(fakewidget.New(widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget})), 1338 + ), 1339 + Right( 1340 + SplitHorizontal( 1341 + Top( 1342 + Border(linestyle.Light), 1343 + PlaceWidget(fakewidget.New(widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget})), 1344 + ), 1345 + Bottom( 1346 + PlaceWidget(fakewidget.New(widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget})), 1347 + ), 1348 + ), 1349 + ), 1350 + ), 1351 + ) 1352 + if err != nil { 1353 + return nil, err 1354 + } 1355 + // Increase the width back to 50 so the mouse clicks land on the "new" area. 1356 + if err := ft.Resize(image.Point{50, 20}); err != nil { 1357 + return nil, err 1358 + } 1359 + // Draw once so the container has a chance to update the tracked area. 1360 + if err := c.Draw(); err != nil { 1361 + return nil, err 1362 + } 1363 + return c, nil 1364 + }, 1365 + events: []terminalapi.Event{ 1366 + &terminalapi.Mouse{Position: image.Point{48, 8}, Button: mouse.ButtonLeft}, 1367 + &terminalapi.Mouse{Position: image.Point{48, 8}, Button: mouse.ButtonRelease}, 1368 + }, 1369 + want: func(size image.Point) *faketerm.Terminal { 1370 + ft := faketerm.MustNew(size) 1371 + // The yellow border signifies that the container was focused. 1372 + cvs := testcanvas.MustNew(ft.Area()) 1373 + testdraw.MustBorder( 1374 + cvs, 1375 + image.Rect(25, 0, 50, 10), 1376 + draw.BorderCellOpts(cell.FgColor(cell.ColorYellow)), 1377 + ) 1378 + testcanvas.MustApply(cvs, ft) 1379 + 1380 + // Widgets that aren't targeted don't get the mouse clicks. 1381 + fakewidget.MustDraw( 1382 + ft, 1383 + testcanvas.MustNew(image.Rect(0, 0, 25, 20)), 1384 + widgetapi.Options{}, 1385 + ) 1386 + fakewidget.MustDraw( 1387 + ft, 1388 + testcanvas.MustNew(image.Rect(25, 10, 50, 20)), 1389 + widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget}, 1390 + &terminalapi.Keyboard{}, 1391 + ) 1392 + 1393 + // The target widget receives the mouse event. 1394 + fakewidget.MustDraw( 1395 + ft, 1396 + testcanvas.MustNew(image.Rect(26, 1, 49, 9)), 1397 + widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget}, 1398 + &terminalapi.Mouse{Position: image.Point{22, 7}, Button: mouse.ButtonLeft}, 1399 + &terminalapi.Mouse{Position: image.Point{22, 7}, Button: mouse.ButtonRelease}, 1400 + ) 1401 + return ft 1402 + }, 937 1403 }, 938 1404 { 939 1405 desc: "event not forwarded if the widget didn't request it", skipped 17 lines 957 1423 ) 958 1424 return ft 959 1425 }, 960 - wantProcessed: 1, 961 1426 }, 962 1427 { 963 1428 desc: "MouseScopeWidget, event not forwarded if it falls on the container's border", skipped 28 lines 992 1457 ) 993 1458 return ft 994 1459 }, 995 - wantProcessed: 2, 996 1460 }, 997 1461 { 998 1462 desc: "MouseScopeContainer, event forwarded if it falls on the container's border", skipped 29 lines 1028 1492 ) 1029 1493 return ft 1030 1494 }, 1031 - wantProcessed: 2, 1032 1495 }, 1033 1496 { 1034 1497 desc: "MouseScopeGlobal, event forwarded if it falls on the container's border", skipped 29 lines 1064 1527 ) 1065 1528 return ft 1066 1529 }, 1067 - wantProcessed: 2, 1068 1530 }, 1069 1531 { 1070 1532 desc: "MouseScopeWidget event not forwarded if it falls outside of widget's canvas", skipped 24 lines 1095 1557 ) 1096 1558 return ft 1097 1559 }, 1098 - wantProcessed: 2, 1099 1560 }, 1100 1561 { 1101 1562 desc: "MouseScopeContainer event forwarded if it falls outside of widget's canvas", skipped 25 lines 1127 1588 ) 1128 1589 return ft 1129 1590 }, 1130 - wantProcessed: 2, 1131 1591 }, 1132 1592 { 1133 1593 desc: "MouseScopeGlobal event forwarded if it falls outside of widget's canvas", skipped 25 lines 1159 1619 ) 1160 1620 return ft 1161 1621 }, 1162 - wantProcessed: 2, 1163 1622 }, 1164 1623 { 1165 1624 desc: "MouseScopeWidget event not forwarded if it falls to another container", skipped 29 lines 1195 1654 ) 1196 1655 return ft 1197 1656 }, 1198 - wantProcessed: 2, 1199 1657 }, 1200 1658 { 1201 1659 desc: "MouseScopeContainer event not forwarded if it falls to another container", skipped 29 lines 1231 1689 ) 1232 1690 return ft 1233 1691 }, 1234 - wantProcessed: 2, 1235 1692 }, 1236 1693 { 1237 1694 desc: "MouseScopeGlobal event forwarded if it falls to another container", skipped 30 lines 1268 1725 ) 1269 1726 return ft 1270 1727 }, 1271 - wantProcessed: 2, 1272 1728 }, 1273 1729 { 1274 1730 desc: "mouse position adjusted relative to widget's canvas, vertical offset", skipped 25 lines 1300 1756 ) 1301 1757 return ft 1302 1758 }, 1303 - wantProcessed: 2, 1304 1759 }, 1305 1760 { 1306 - desc: "mouse poisition adjusted relative to widget's canvas, horizontal offset", 1761 + desc: "mouse position adjusted relative to widget's canvas, horizontal offset", 1307 1762 termSize: image.Point{30, 20}, 1308 1763 container: func(ft *faketerm.Terminal) (*Container, error) { 1309 1764 return New( skipped 22 lines 1332 1787 ) 1333 1788 return ft 1334 1789 }, 1335 - wantProcessed: 2, 1336 1790 }, 1337 1791 { 1338 1792 desc: "widget returns an error when processing the event", skipped 7 lines 1346 1800 events: []terminalapi.Event{ 1347 1801 &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRight}, 1348 1802 }, 1803 + wantProcessed: 2, // The error is also an event. 1349 1804 want: func(size image.Point) *faketerm.Terminal { 1350 1805 ft := faketerm.MustNew(size) 1351 1806 skipped 4 lines 1356 1811 ) 1357 1812 return ft 1358 1813 }, 1359 - wantProcessed: 3, 1360 - wantErr: true, 1814 + wantErr: true, 1361 1815 }, 1362 1816 } 1363 1817 skipped 16 lines 1380 1834 eh.handle(ev.(*terminalapi.Error).Error()) 1381 1835 }) 1382 1836 c.Subscribe(eds) 1837 + // Initial draw to determine sizes of containers. 1838 + if err := c.Draw(); err != nil { 1839 + t.Fatalf("Draw => unexpected error: %v", err) 1840 + } 1383 1841 for _, ev := range tc.events { 1384 1842 eds.Event(ev) 1385 1843 } 1844 + var wantEv int 1845 + if tc.wantProcessed != 0 { 1846 + wantEv = tc.wantProcessed 1847 + } else { 1848 + wantEv = len(tc.events) 1849 + } 1850 + 1386 1851 if err := testevent.WaitFor(5*time.Second, func() error { 1387 - if got, want := eds.Processed(), tc . wantProcessed ; got != want { 1852 + if got, want := eds.Processed(), wantEv ; got != want { 1388 1853 return fmt.Errorf("the event distribution system processed %d events, want %d", got, want) 1389 1854 } 1390 1855 return nil skipped 16 lines 1407 1872 } 1408 1873 } 1409 1874 1875 + func TestUpdate(t *testing.T) { 1876 + tests := []struct { 1877 + desc string 1878 + termSize image.Point 1879 + container func(ft *faketerm.Terminal) (*Container, error) 1880 + updateID string 1881 + updateOpts []Option 1882 + // events are events delivered before the update. 1883 + beforeEvents []terminalapi.Event 1884 + // events are events delivered after the update. 1885 + afterEvents []terminalapi.Event 1886 + wantUpdateErr bool 1887 + want func(size image.Point) *faketerm.Terminal 1888 + }{ 1889 + { 1890 + desc: "fails on empty updateID", 1891 + termSize: image.Point{10, 10}, 1892 + container: func(ft *faketerm.Terminal) (*Container, error) { 1893 + return New(ft) 1894 + }, 1895 + wantUpdateErr: true, 1896 + }, 1897 + { 1898 + desc: "fails when no container with the ID is found", 1899 + termSize: image.Point{10, 10}, 1900 + container: func(ft *faketerm.Terminal) (*Container, error) { 1901 + return New(ft) 1902 + }, 1903 + updateID: "myID", 1904 + wantUpdateErr: true, 1905 + }, 1906 + { 1907 + desc: "no changes when no options are provided", 1908 + termSize: image.Point{10, 10}, 1909 + container: func(ft *faketerm.Terminal) (*Container, error) { 1910 + return New( 1911 + ft, 1912 + ID("myID"), 1913 + Border(linestyle.Light), 1914 + ) 1915 + }, 1916 + updateID: "myID", 1917 + want: func(size image.Point) *faketerm.Terminal { 1918 + ft := faketerm.MustNew(size) 1919 + cvs := testcanvas.MustNew(ft.Area()) 1920 + testdraw.MustBorder( 1921 + cvs, 1922 + image.Rect(0, 0, 10, 10), 1923 + draw.BorderCellOpts(cell.FgColor(cell.ColorYellow)), 1924 + ) 1925 + testcanvas.MustApply(cvs, ft) 1926 + return ft 1927 + }, 1928 + }, 1929 + { 1930 + desc: "fails on invalid options", 1931 + termSize: image.Point{10, 10}, 1932 + container: func(ft *faketerm.Terminal) (*Container, error) { 1933 + return New( 1934 + ft, 1935 + ID("myID"), 1936 + Border(linestyle.Light), 1937 + ) 1938 + }, 1939 + updateID: "myID", 1940 + updateOpts: []Option{ 1941 + MarginTop(-1), 1942 + }, 1943 + wantUpdateErr: true, 1944 + }, 1945 + { 1946 + desc: "fails when update introduces a duplicate ID", 1947 + termSize: image.Point{10, 10}, 1948 + container: func(ft *faketerm.Terminal) (*Container, error) { 1949 + return New( 1950 + ft, 1951 + ID("myID"), 1952 + Border(linestyle.Light), 1953 + ) 1954 + }, 1955 + updateID: "myID", 1956 + updateOpts: []Option{ 1957 + SplitVertical( 1958 + Left( 1959 + ID("left"), 1960 + ), 1961 + Right( 1962 + ID("myID"), 1963 + ), 1964 + ), 1965 + }, 1966 + wantUpdateErr: true, 1967 + }, 1968 + { 1969 + desc: "removes border from the container", 1970 + termSize: image.Point{10, 10}, 1971 + container: func(ft *faketerm.Terminal) (*Container, error) { 1972 + return New( 1973 + ft, 1974 + ID("myID"), 1975 + Border(linestyle.Light), 1976 + ) 1977 + }, 1978 + updateID: "myID", 1979 + updateOpts: []Option{ 1980 + Border(linestyle.None), 1981 + }, 1982 + want: func(size image.Point) *faketerm.Terminal { 1983 + return faketerm.MustNew(size) 1984 + }, 1985 + }, 1986 + { 1987 + desc: "places widget into a sub-container container", 1988 + termSize: image.Point{20, 10}, 1989 + container: func(ft *faketerm.Terminal) (*Container, error) { 1990 + return New( 1991 + ft, 1992 + ID("myRoot"), 1993 + SplitVertical( 1994 + Left( 1995 + ID("left"), 1996 + ), 1997 + Right( 1998 + ID("right"), 1999 + ), 2000 + ), 2001 + ) 2002 + }, 2003 + updateID: "right", 2004 + updateOpts: []Option{ 2005 + PlaceWidget(fakewidget.New(widgetapi.Options{})), 2006 + }, 2007 + want: func(size image.Point) *faketerm.Terminal { 2008 + ft := faketerm.MustNew(size) 2009 + cvs := testcanvas.MustNew(ft.Area()) 2010 + wAr := image.Rect(10, 0, 20, 10) 2011 + wCvs := testcanvas.MustNew(wAr) 2012 + fakewidget.MustDraw(ft, wCvs, widgetapi.Options{}) 2013 + testcanvas.MustCopyTo(wCvs, cvs) 2014 + testcanvas.MustApply(cvs, ft) 2015 + return ft 2016 + }, 2017 + }, 2018 + { 2019 + desc: "places widget into root which removes children", 2020 + termSize: image.Point{20, 10}, 2021 + container: func(ft *faketerm.Terminal) (*Container, error) { 2022 + return New( 2023 + ft, 2024 + ID("myRoot"), 2025 + SplitVertical( 2026 + Left( 2027 + ID("left"), 2028 + Border(linestyle.Light), 2029 + ), 2030 + Right( 2031 + ID("right"), 2032 + Border(linestyle.Light), 2033 + ), 2034 + ), 2035 + ) 2036 + }, 2037 + updateID: "myRoot", 2038 + updateOpts: []Option{ 2039 + PlaceWidget(fakewidget.New(widgetapi.Options{})), 2040 + }, 2041 + want: func(size image.Point) *faketerm.Terminal { 2042 + ft := faketerm.MustNew(size) 2043 + cvs := testcanvas.MustNew(ft.Area()) 2044 + fakewidget.MustDraw(ft, cvs, widgetapi.Options{}) 2045 + testcanvas.MustApply(cvs, ft) 2046 + return ft 2047 + }, 2048 + }, 2049 + { 2050 + desc: "changes container splits", 2051 + termSize: image.Point{10, 10}, 2052 + container: func(ft *faketerm.Terminal) (*Container, error) { 2053 + return New( 2054 + ft, 2055 + ID("myRoot"), 2056 + SplitVertical( 2057 + Left( 2058 + ID("left"), 2059 + Border(linestyle.Light), 2060 + ), 2061 + Right( 2062 + ID("right"), 2063 + Border(linestyle.Light), 2064 + ), 2065 + ), 2066 + ) 2067 + }, 2068 + updateID: "myRoot", 2069 + updateOpts: []Option{ 2070 + SplitHorizontal( 2071 + Top( 2072 + ID("left"), 2073 + Border(linestyle.Light), 2074 + ), 2075 + Bottom( 2076 + ID("right"), 2077 + Border(linestyle.Light), 2078 + ), 2079 + ), 2080 + }, 2081 + want: func(size image.Point) *faketerm.Terminal { 2082 + ft := faketerm.MustNew(size) 2083 + cvs := testcanvas.MustNew(ft.Area()) 2084 + testdraw.MustBorder(cvs, image.Rect(0, 0, 10, 5)) 2085 + testdraw.MustBorder(cvs, image.Rect(0, 5, 10, 10)) 2086 + testcanvas.MustApply(cvs, ft) 2087 + return ft 2088 + }, 2089 + }, 2090 + { 2091 + desc: "update retains original focused container if it still exists", 2092 + termSize: image.Point{10, 10}, 2093 + container: func(ft *faketerm.Terminal) (*Container, error) { 2094 + return New( 2095 + ft, 2096 + ID("myRoot"), 2097 + SplitVertical( 2098 + Left( 2099 + ID("left"), 2100 + Border(linestyle.Light), 2101 + ), 2102 + Right( 2103 + ID("right"), 2104 + Border(linestyle.Light), 2105 + SplitHorizontal( 2106 + Top( 2107 + ID("rightTop"), 2108 + Border(linestyle.Light), 2109 + ), 2110 + Bottom( 2111 + ID("rightBottom"), 2112 + Border(linestyle.Light), 2113 + ), 2114 + ), 2115 + ), 2116 + ), 2117 + ) 2118 + }, 2119 + beforeEvents: []terminalapi.Event{ 2120 + // Move focus to container with ID "right". 2121 + // It will continue to exist after the update. 2122 + &terminalapi.Mouse{Position: image.Point{5, 0}, Button: mouse.ButtonLeft}, 2123 + &terminalapi.Mouse{Position: image.Point{5, 0}, Button: mouse.ButtonRelease}, 2124 + }, 2125 + updateID: "right", 2126 + updateOpts: []Option{ 2127 + Clear(), 2128 + }, 2129 + want: func(size image.Point) *faketerm.Terminal { 2130 + ft := faketerm.MustNew(size) 2131 + cvs := testcanvas.MustNew(ft.Area()) 2132 + testdraw.MustBorder(cvs, image.Rect(0, 0, 5, 10)) 2133 + testdraw.MustBorder(cvs, image.Rect(5, 0, 10, 10), draw.BorderCellOpts(cell.FgColor(cell.ColorYellow))) 2134 + testcanvas.MustApply(cvs, ft) 2135 + return ft 2136 + }, 2137 + }, 2138 + { 2139 + desc: "update moves focus to the nearest parent when focused container is destroyed", 2140 + termSize: image.Point{10, 10}, 2141 + container: func(ft *faketerm.Terminal) (*Container, error) { 2142 + return New( 2143 + ft, 2144 + ID("myRoot"), 2145 + SplitVertical( 2146 + Left( 2147 + ID("left"), 2148 + Border(linestyle.Light), 2149 + ), 2150 + Right( 2151 + ID("right"), 2152 + Border(linestyle.Light), 2153 + SplitHorizontal( 2154 + Top( 2155 + ID("rightTop"), 2156 + Border(linestyle.Light), 2157 + ), 2158 + Bottom( 2159 + ID("rightBottom"), 2160 + Border(linestyle.Light), 2161 + ), 2162 + ), 2163 + ), 2164 + ), 2165 + ) 2166 + }, 2167 + beforeEvents: []terminalapi.Event{ 2168 + // Move focus to container with ID "rightTop". 2169 + // It will be destroyed by calling update. 2170 + &terminalapi.Mouse{Position: image.Point{6, 1}, Button: mouse.ButtonLeft}, 2171 + &terminalapi.Mouse{Position: image.Point{6, 1}, Button: mouse.ButtonRelease}, 2172 + }, 2173 + updateID: "right", 2174 + updateOpts: []Option{ 2175 + Clear(), 2176 + }, 2177 + want: func(size image.Point) *faketerm.Terminal { 2178 + ft := faketerm.MustNew(size) 2179 + cvs := testcanvas.MustNew(ft.Area()) 2180 + testdraw.MustBorder(cvs, image.Rect(0, 0, 5, 10)) 2181 + testdraw.MustBorder(cvs, image.Rect(5, 0, 10, 10), draw.BorderCellOpts(cell.FgColor(cell.ColorYellow))) 2182 + testcanvas.MustApply(cvs, ft) 2183 + return ft 2184 + }, 2185 + }, 2186 + { 2187 + desc: "newly placed widget gets keyboard events", 2188 + termSize: image.Point{10, 10}, 2189 + container: func(ft *faketerm.Terminal) (*Container, error) { 2190 + return New( 2191 + ft, 2192 + ID("myRoot"), 2193 + PlaceWidget(fakewidget.New(widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused})), 2194 + ) 2195 + }, 2196 + beforeEvents: []terminalapi.Event{ 2197 + // Move focus to the target container. 2198 + &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft}, 2199 + &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease}, 2200 + }, 2201 + afterEvents: []terminalapi.Event{ 2202 + // Send the keyboard event. 2203 + &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 2204 + }, 2205 + updateID: "myRoot", 2206 + updateOpts: []Option{ 2207 + PlaceWidget(fakewidget.New(widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused})), 2208 + }, 2209 + want: func(size image.Point) *faketerm.Terminal { 2210 + ft := faketerm.MustNew(size) 2211 + cvs := testcanvas.MustNew(ft.Area()) 2212 + fakewidget.MustDraw( 2213 + ft, 2214 + cvs, 2215 + widgetapi.Options{WantKeyboard: widgetapi.KeyScopeFocused}, 2216 + &terminalapi.Keyboard{Key: keyboard.KeyEnter}, 2217 + ) 2218 + testcanvas.MustApply(cvs, ft) 2219 + return ft 2220 + }, 2221 + }, 2222 + { 2223 + desc: "newly placed widget gets mouse events", 2224 + termSize: image.Point{20, 10}, 2225 + container: func(ft *faketerm.Terminal) (*Container, error) { 2226 + return New( 2227 + ft, 2228 + ID("myRoot"), 2229 + PlaceWidget(fakewidget.New(widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget})), 2230 + ) 2231 + }, 2232 + afterEvents: []terminalapi.Event{ 2233 + &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonLeft}, 2234 + &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease}, 2235 + }, 2236 + updateID: "myRoot", 2237 + updateOpts: []Option{ 2238 + PlaceWidget(fakewidget.New(widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget})), 2239 + }, 2240 + want: func(size image.Point) *faketerm.Terminal { 2241 + ft := faketerm.MustNew(size) 2242 + cvs := testcanvas.MustNew(ft.Area()) 2243 + fakewidget.MustDraw( 2244 + ft, 2245 + cvs, 2246 + widgetapi.Options{WantMouse: widgetapi.MouseScopeWidget}, 2247 + &terminalapi.Mouse{Position: image.Point{0, 0}, Button: mouse.ButtonRelease}, 2248 + ) 2249 + testcanvas.MustApply(cvs, ft) 2250 + return ft 2251 + }, 2252 + }, 2253 + } 2254 + 2255 + for _, tc := range tests { 2256 + t.Run(tc.desc, func(t *testing.T) { 2257 + got, err := faketerm.New(tc.termSize) 2258 + if err != nil { 2259 + t.Fatalf("faketerm.New => unexpected error: %v", err) 2260 + } 2261 + 2262 + cont, err := tc.container(got) 2263 + if err != nil { 2264 + t.Fatalf("tc.container => unexpected error: %v", err) 2265 + } 2266 + 2267 + eds := event.NewDistributionSystem() 2268 + eh := &errorHandler{} 2269 + // Subscribe to receive errors. 2270 + eds.Subscribe([]terminalapi.Event{terminalapi.NewError("")}, func(ev terminalapi.Event) { 2271 + eh.handle(ev.(*terminalapi.Error).Error()) 2272 + }) 2273 + cont.Subscribe(eds) 2274 + // Initial draw to determine sizes of containers. 2275 + if err := cont.Draw(); err != nil { 2276 + t.Fatalf("Draw => unexpected error: %v", err) 2277 + } 2278 + 2279 + // Deliver the before events. 2280 + for _, ev := range tc.beforeEvents { 2281 + eds.Event(ev) 2282 + } 2283 + if err := testevent.WaitFor(5*time.Second, func() error { 2284 + if got, want := eds.Processed(), len(tc.beforeEvents); got != want { 2285 + return fmt.Errorf("the event distribution system processed %d events, want %d", got, want) 2286 + } 2287 + return nil 2288 + }); err != nil { 2289 + t.Fatalf("testevent.WaitFor => %v", err) 2290 + } 2291 + 2292 + { 2293 + err := cont.Update(tc.updateID, tc.updateOpts...) 2294 + if (err != nil) != tc.wantUpdateErr { 2295 + t.Errorf("Update => unexpected error:%v, wantErr:%v", err, tc.wantUpdateErr) 2296 + } 2297 + if err != nil { 2298 + return 2299 + } 2300 + } 2301 + 2302 + // Deliver the after events. 2303 + for _, ev := range tc.afterEvents { 2304 + eds.Event(ev) 2305 + } 2306 + wantEv := len(tc.beforeEvents) + len(tc.afterEvents) 2307 + if err := testevent.WaitFor(5*time.Second, func() error { 2308 + if got, want := eds.Processed(), wantEv; got != want { 2309 + return fmt.Errorf("the event distribution system processed %d events, want %d", got, want) 2310 + } 2311 + return nil 2312 + }); err != nil { 2313 + t.Fatalf("testevent.WaitFor => %v", err) 2314 + } 2315 + 2316 + if err := cont.Draw(); err != nil { 2317 + t.Fatalf("Draw => unexpected error: %v", err) 2318 + } 2319 + 2320 + var want *faketerm.Terminal 2321 + if tc.want != nil { 2322 + want = tc.want(tc.termSize) 2323 + } else { 2324 + w, err := faketerm.New(tc.termSize) 2325 + if err != nil { 2326 + t.Fatalf("faketerm.New => unexpected error: %v", err) 2327 + } 2328 + want = w 2329 + } 2330 + if diff := faketerm.Diff(want, got); diff != "" { 2331 + t.Errorf("Draw => %v", diff) 2332 + } 2333 + }) 2334 + } 2335 + 2336 + } 2337 +