fsm_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. // Copyright (c) 2013 - Max Persson <max@looplab.se>
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package fsm
  15. import (
  16. "fmt"
  17. "sync"
  18. "testing"
  19. "time"
  20. )
  21. type fakeTransitionerObj struct {
  22. }
  23. func (t fakeTransitionerObj) transition(f *FSM) error {
  24. return &InternalError{}
  25. }
  26. func TestSameState(t *testing.T) {
  27. fsm := NewFSM(
  28. "start",
  29. Events{
  30. {Name: "run", Src: []string{"start"}, Dst: "start"},
  31. },
  32. Callbacks{},
  33. )
  34. fsm.Event("run")
  35. if fsm.Current() != "start" {
  36. t.Error("expected state to be 'start'")
  37. }
  38. }
  39. func TestSetState(t *testing.T) {
  40. fsm := NewFSM(
  41. "walking",
  42. Events{
  43. {Name: "walk", Src: []string{"start"}, Dst: "walking"},
  44. },
  45. Callbacks{},
  46. )
  47. fsm.SetState("start")
  48. if fsm.Current() != "start" {
  49. t.Error("expected state to be 'walking'")
  50. }
  51. err := fsm.Event("walk")
  52. if err != nil {
  53. t.Error("transition is expected no error")
  54. }
  55. }
  56. func TestBadTransition(t *testing.T) {
  57. fsm := NewFSM(
  58. "start",
  59. Events{
  60. {Name: "run", Src: []string{"start"}, Dst: "running"},
  61. },
  62. Callbacks{},
  63. )
  64. fsm.transitionerObj = new(fakeTransitionerObj)
  65. err := fsm.Event("run")
  66. if err == nil {
  67. t.Error("bad transition should give an error")
  68. }
  69. }
  70. func TestInappropriateEvent(t *testing.T) {
  71. fsm := NewFSM(
  72. "closed",
  73. Events{
  74. {Name: "open", Src: []string{"closed"}, Dst: "open"},
  75. {Name: "close", Src: []string{"open"}, Dst: "closed"},
  76. },
  77. Callbacks{},
  78. )
  79. err := fsm.Event("close")
  80. if e, ok := err.(InvalidEventError); !ok && e.Event != "close" && e.State != "closed" {
  81. t.Error("expected 'InvalidEventError' with correct state and event")
  82. }
  83. }
  84. func TestInvalidEvent(t *testing.T) {
  85. fsm := NewFSM(
  86. "closed",
  87. Events{
  88. {Name: "open", Src: []string{"closed"}, Dst: "open"},
  89. {Name: "close", Src: []string{"open"}, Dst: "closed"},
  90. },
  91. Callbacks{},
  92. )
  93. err := fsm.Event("lock")
  94. if e, ok := err.(UnknownEventError); !ok && e.Event != "close" {
  95. t.Error("expected 'UnknownEventError' with correct event")
  96. }
  97. }
  98. func TestMultipleSources(t *testing.T) {
  99. fsm := NewFSM(
  100. "one",
  101. Events{
  102. {Name: "first", Src: []string{"one"}, Dst: "two"},
  103. {Name: "second", Src: []string{"two"}, Dst: "three"},
  104. {Name: "reset", Src: []string{"one", "two", "three"}, Dst: "one"},
  105. },
  106. Callbacks{},
  107. )
  108. fsm.Event("first")
  109. if fsm.Current() != "two" {
  110. t.Error("expected state to be 'two'")
  111. }
  112. fsm.Event("reset")
  113. if fsm.Current() != "one" {
  114. t.Error("expected state to be 'one'")
  115. }
  116. fsm.Event("first")
  117. fsm.Event("second")
  118. if fsm.Current() != "three" {
  119. t.Error("expected state to be 'three'")
  120. }
  121. fsm.Event("reset")
  122. if fsm.Current() != "one" {
  123. t.Error("expected state to be 'one'")
  124. }
  125. }
  126. func TestMultipleEvents(t *testing.T) {
  127. fsm := NewFSM(
  128. "start",
  129. Events{
  130. {Name: "first", Src: []string{"start"}, Dst: "one"},
  131. {Name: "second", Src: []string{"start"}, Dst: "two"},
  132. {Name: "reset", Src: []string{"one"}, Dst: "reset_one"},
  133. {Name: "reset", Src: []string{"two"}, Dst: "reset_two"},
  134. {Name: "reset", Src: []string{"reset_one", "reset_two"}, Dst: "start"},
  135. },
  136. Callbacks{},
  137. )
  138. fsm.Event("first")
  139. fsm.Event("reset")
  140. if fsm.Current() != "reset_one" {
  141. t.Error("expected state to be 'reset_one'")
  142. }
  143. fsm.Event("reset")
  144. if fsm.Current() != "start" {
  145. t.Error("expected state to be 'start'")
  146. }
  147. fsm.Event("second")
  148. fsm.Event("reset")
  149. if fsm.Current() != "reset_two" {
  150. t.Error("expected state to be 'reset_two'")
  151. }
  152. fsm.Event("reset")
  153. if fsm.Current() != "start" {
  154. t.Error("expected state to be 'start'")
  155. }
  156. }
  157. func TestGenericCallbacks(t *testing.T) {
  158. beforeEvent := false
  159. leaveState := false
  160. enterState := false
  161. afterEvent := false
  162. fsm := NewFSM(
  163. "start",
  164. Events{
  165. {Name: "run", Src: []string{"start"}, Dst: "end"},
  166. },
  167. Callbacks{
  168. "before_event": func(e *Event) {
  169. beforeEvent = true
  170. },
  171. "leave_state": func(e *Event) {
  172. leaveState = true
  173. },
  174. "enter_state": func(e *Event) {
  175. enterState = true
  176. },
  177. "after_event": func(e *Event) {
  178. afterEvent = true
  179. },
  180. },
  181. )
  182. fsm.Event("run")
  183. if !(beforeEvent && leaveState && enterState && afterEvent) {
  184. t.Error("expected all callbacks to be called")
  185. }
  186. }
  187. func TestSpecificCallbacks(t *testing.T) {
  188. beforeEvent := false
  189. leaveState := false
  190. enterState := false
  191. afterEvent := false
  192. fsm := NewFSM(
  193. "start",
  194. Events{
  195. {Name: "run", Src: []string{"start"}, Dst: "end"},
  196. },
  197. Callbacks{
  198. "before_run": func(e *Event) {
  199. beforeEvent = true
  200. },
  201. "leave_start": func(e *Event) {
  202. leaveState = true
  203. },
  204. "enter_end": func(e *Event) {
  205. enterState = true
  206. },
  207. "after_run": func(e *Event) {
  208. afterEvent = true
  209. },
  210. },
  211. )
  212. fsm.Event("run")
  213. if !(beforeEvent && leaveState && enterState && afterEvent) {
  214. t.Error("expected all callbacks to be called")
  215. }
  216. }
  217. func TestSpecificCallbacksShortform(t *testing.T) {
  218. enterState := false
  219. afterEvent := false
  220. fsm := NewFSM(
  221. "start",
  222. Events{
  223. {Name: "run", Src: []string{"start"}, Dst: "end"},
  224. },
  225. Callbacks{
  226. "end": func(e *Event) {
  227. enterState = true
  228. },
  229. "run": func(e *Event) {
  230. afterEvent = true
  231. },
  232. },
  233. )
  234. fsm.Event("run")
  235. if !(enterState && afterEvent) {
  236. t.Error("expected all callbacks to be called")
  237. }
  238. }
  239. func TestBeforeEventWithoutTransition(t *testing.T) {
  240. beforeEvent := true
  241. fsm := NewFSM(
  242. "start",
  243. Events{
  244. {Name: "dontrun", Src: []string{"start"}, Dst: "start"},
  245. },
  246. Callbacks{
  247. "before_event": func(e *Event) {
  248. beforeEvent = true
  249. },
  250. },
  251. )
  252. err := fsm.Event("dontrun")
  253. if e, ok := err.(NoTransitionError); !ok && e.Err != nil {
  254. t.Error("expected 'NoTransitionError' without custom error")
  255. }
  256. if fsm.Current() != "start" {
  257. t.Error("expected state to be 'start'")
  258. }
  259. if !beforeEvent {
  260. t.Error("expected callback to be called")
  261. }
  262. }
  263. func TestCancelBeforeGenericEvent(t *testing.T) {
  264. fsm := NewFSM(
  265. "start",
  266. Events{
  267. {Name: "run", Src: []string{"start"}, Dst: "end"},
  268. },
  269. Callbacks{
  270. "before_event": func(e *Event) {
  271. e.Cancel()
  272. },
  273. },
  274. )
  275. fsm.Event("run")
  276. if fsm.Current() != "start" {
  277. t.Error("expected state to be 'start'")
  278. }
  279. }
  280. func TestCancelBeforeSpecificEvent(t *testing.T) {
  281. fsm := NewFSM(
  282. "start",
  283. Events{
  284. {Name: "run", Src: []string{"start"}, Dst: "end"},
  285. },
  286. Callbacks{
  287. "before_run": func(e *Event) {
  288. e.Cancel()
  289. },
  290. },
  291. )
  292. fsm.Event("run")
  293. if fsm.Current() != "start" {
  294. t.Error("expected state to be 'start'")
  295. }
  296. }
  297. func TestCancelLeaveGenericState(t *testing.T) {
  298. fsm := NewFSM(
  299. "start",
  300. Events{
  301. {Name: "run", Src: []string{"start"}, Dst: "end"},
  302. },
  303. Callbacks{
  304. "leave_state": func(e *Event) {
  305. e.Cancel()
  306. },
  307. },
  308. )
  309. fsm.Event("run")
  310. if fsm.Current() != "start" {
  311. t.Error("expected state to be 'start'")
  312. }
  313. }
  314. func TestCancelLeaveSpecificState(t *testing.T) {
  315. fsm := NewFSM(
  316. "start",
  317. Events{
  318. {Name: "run", Src: []string{"start"}, Dst: "end"},
  319. },
  320. Callbacks{
  321. "leave_start": func(e *Event) {
  322. e.Cancel()
  323. },
  324. },
  325. )
  326. fsm.Event("run")
  327. if fsm.Current() != "start" {
  328. t.Error("expected state to be 'start'")
  329. }
  330. }
  331. func TestCancelWithError(t *testing.T) {
  332. fsm := NewFSM(
  333. "start",
  334. Events{
  335. {Name: "run", Src: []string{"start"}, Dst: "end"},
  336. },
  337. Callbacks{
  338. "before_event": func(e *Event) {
  339. e.Cancel(fmt.Errorf("error"))
  340. },
  341. },
  342. )
  343. err := fsm.Event("run")
  344. if _, ok := err.(CanceledError); !ok {
  345. t.Error("expected only 'CanceledError'")
  346. }
  347. if e, ok := err.(CanceledError); ok && e.Err.Error() != "error" {
  348. t.Error("expected 'CanceledError' with correct custom error")
  349. }
  350. if fsm.Current() != "start" {
  351. t.Error("expected state to be 'start'")
  352. }
  353. }
  354. func TestAsyncTransitionGenericState(t *testing.T) {
  355. fsm := NewFSM(
  356. "start",
  357. Events{
  358. {Name: "run", Src: []string{"start"}, Dst: "end"},
  359. },
  360. Callbacks{
  361. "leave_state": func(e *Event) {
  362. e.Async()
  363. },
  364. },
  365. )
  366. fsm.Event("run")
  367. if fsm.Current() != "start" {
  368. t.Error("expected state to be 'start'")
  369. }
  370. fsm.Transition()
  371. if fsm.Current() != "end" {
  372. t.Error("expected state to be 'end'")
  373. }
  374. }
  375. func TestAsyncTransitionSpecificState(t *testing.T) {
  376. fsm := NewFSM(
  377. "start",
  378. Events{
  379. {Name: "run", Src: []string{"start"}, Dst: "end"},
  380. },
  381. Callbacks{
  382. "leave_start": func(e *Event) {
  383. e.Async()
  384. },
  385. },
  386. )
  387. fsm.Event("run")
  388. if fsm.Current() != "start" {
  389. t.Error("expected state to be 'start'")
  390. }
  391. fsm.Transition()
  392. if fsm.Current() != "end" {
  393. t.Error("expected state to be 'end'")
  394. }
  395. }
  396. func TestAsyncTransitionInProgress(t *testing.T) {
  397. fsm := NewFSM(
  398. "start",
  399. Events{
  400. {Name: "run", Src: []string{"start"}, Dst: "end"},
  401. {Name: "reset", Src: []string{"end"}, Dst: "start"},
  402. },
  403. Callbacks{
  404. "leave_start": func(e *Event) {
  405. e.Async()
  406. },
  407. },
  408. )
  409. fsm.Event("run")
  410. err := fsm.Event("reset")
  411. if e, ok := err.(InTransitionError); !ok && e.Event != "reset" {
  412. t.Error("expected 'InTransitionError' with correct state")
  413. }
  414. fsm.Transition()
  415. fsm.Event("reset")
  416. if fsm.Current() != "start" {
  417. t.Error("expected state to be 'start'")
  418. }
  419. }
  420. func TestAsyncTransitionNotInProgress(t *testing.T) {
  421. fsm := NewFSM(
  422. "start",
  423. Events{
  424. {Name: "run", Src: []string{"start"}, Dst: "end"},
  425. {Name: "reset", Src: []string{"end"}, Dst: "start"},
  426. },
  427. Callbacks{},
  428. )
  429. err := fsm.Transition()
  430. if _, ok := err.(NotInTransitionError); !ok {
  431. t.Error("expected 'NotInTransitionError'")
  432. }
  433. }
  434. func TestCallbackNoError(t *testing.T) {
  435. fsm := NewFSM(
  436. "start",
  437. Events{
  438. {Name: "run", Src: []string{"start"}, Dst: "end"},
  439. },
  440. Callbacks{
  441. "run": func(e *Event) {
  442. },
  443. },
  444. )
  445. e := fsm.Event("run")
  446. if e != nil {
  447. t.Error("expected no error")
  448. }
  449. }
  450. func TestCallbackError(t *testing.T) {
  451. fsm := NewFSM(
  452. "start",
  453. Events{
  454. {Name: "run", Src: []string{"start"}, Dst: "end"},
  455. },
  456. Callbacks{
  457. "run": func(e *Event) {
  458. e.Err = fmt.Errorf("error")
  459. },
  460. },
  461. )
  462. e := fsm.Event("run")
  463. if e.Error() != "error" {
  464. t.Error("expected error to be 'error'")
  465. }
  466. }
  467. func TestCallbackArgs(t *testing.T) {
  468. fsm := NewFSM(
  469. "start",
  470. Events{
  471. {Name: "run", Src: []string{"start"}, Dst: "end"},
  472. },
  473. Callbacks{
  474. "run": func(e *Event) {
  475. if len(e.Args) != 1 {
  476. t.Error("too few arguments")
  477. }
  478. arg, ok := e.Args[0].(string)
  479. if !ok {
  480. t.Error("not a string argument")
  481. }
  482. if arg != "test" {
  483. t.Error("incorrect argument")
  484. }
  485. },
  486. },
  487. )
  488. fsm.Event("run", "test")
  489. }
  490. func TestNoDeadLock(t *testing.T) {
  491. var fsm *FSM
  492. fsm = NewFSM(
  493. "start",
  494. Events{
  495. {Name: "run", Src: []string{"start"}, Dst: "end"},
  496. },
  497. Callbacks{
  498. "run": func(e *Event) {
  499. fsm.Current() // Should not result in a panic / deadlock
  500. },
  501. },
  502. )
  503. fsm.Event("run")
  504. }
  505. func TestThreadSafetyRaceCondition(t *testing.T) {
  506. fsm := NewFSM(
  507. "start",
  508. Events{
  509. {Name: "run", Src: []string{"start"}, Dst: "end"},
  510. },
  511. Callbacks{
  512. "run": func(e *Event) {
  513. },
  514. },
  515. )
  516. var wg sync.WaitGroup
  517. wg.Add(1)
  518. go func() {
  519. defer wg.Done()
  520. _ = fsm.Current()
  521. }()
  522. fsm.Event("run")
  523. wg.Wait()
  524. }
  525. func TestDoubleTransition(t *testing.T) {
  526. var fsm *FSM
  527. var wg sync.WaitGroup
  528. wg.Add(2)
  529. fsm = NewFSM(
  530. "start",
  531. Events{
  532. {Name: "run", Src: []string{"start"}, Dst: "end"},
  533. },
  534. Callbacks{
  535. "before_run": func(e *Event) {
  536. wg.Done()
  537. // Imagine a concurrent event coming in of the same type while
  538. // the data access mutex is unlocked because the current transition
  539. // is running its event callbacks, getting around the "active"
  540. // transition checks
  541. if len(e.Args) == 0 {
  542. // Must be concurrent so the test may pass when we add a mutex that synchronizes
  543. // calls to Event(...). It will then fail as an inappropriate transition as we
  544. // have changed state.
  545. go func() {
  546. if err := fsm.Event("run", "second run"); err != nil {
  547. fmt.Println(err)
  548. wg.Done() // It should fail, and then we unfreeze the test.
  549. }
  550. }()
  551. time.Sleep(20 * time.Millisecond)
  552. } else {
  553. panic("Was able to reissue an event mid-transition")
  554. }
  555. },
  556. },
  557. )
  558. if err := fsm.Event("run"); err != nil {
  559. fmt.Println(err)
  560. }
  561. wg.Wait()
  562. }
  563. func TestNoTransition(t *testing.T) {
  564. fsm := NewFSM(
  565. "start",
  566. Events{
  567. {Name: "run", Src: []string{"start"}, Dst: "start"},
  568. },
  569. Callbacks{},
  570. )
  571. err := fsm.Event("run")
  572. if _, ok := err.(NoTransitionError); !ok {
  573. t.Error("expected 'NoTransitionError'")
  574. }
  575. }
  576. func ExampleNewFSM() {
  577. fsm := NewFSM(
  578. "green",
  579. Events{
  580. {Name: "warn", Src: []string{"green"}, Dst: "yellow"},
  581. {Name: "panic", Src: []string{"yellow"}, Dst: "red"},
  582. {Name: "panic", Src: []string{"green"}, Dst: "red"},
  583. {Name: "calm", Src: []string{"red"}, Dst: "yellow"},
  584. {Name: "clear", Src: []string{"yellow"}, Dst: "green"},
  585. },
  586. Callbacks{
  587. "before_warn": func(e *Event) {
  588. fmt.Println("before_warn")
  589. },
  590. "before_event": func(e *Event) {
  591. fmt.Println("before_event")
  592. },
  593. "leave_green": func(e *Event) {
  594. fmt.Println("leave_green")
  595. },
  596. "leave_state": func(e *Event) {
  597. fmt.Println("leave_state")
  598. },
  599. "enter_yellow": func(e *Event) {
  600. fmt.Println("enter_yellow")
  601. },
  602. "enter_state": func(e *Event) {
  603. fmt.Println("enter_state")
  604. },
  605. "after_warn": func(e *Event) {
  606. fmt.Println("after_warn")
  607. },
  608. "after_event": func(e *Event) {
  609. fmt.Println("after_event")
  610. },
  611. },
  612. )
  613. fmt.Println(fsm.Current())
  614. err := fsm.Event("warn")
  615. if err != nil {
  616. fmt.Println(err)
  617. }
  618. fmt.Println(fsm.Current())
  619. // Output:
  620. // green
  621. // before_warn
  622. // before_event
  623. // leave_green
  624. // leave_state
  625. // enter_yellow
  626. // enter_state
  627. // after_warn
  628. // after_event
  629. // yellow
  630. }
  631. func ExampleFSM_Current() {
  632. fsm := NewFSM(
  633. "closed",
  634. Events{
  635. {Name: "open", Src: []string{"closed"}, Dst: "open"},
  636. {Name: "close", Src: []string{"open"}, Dst: "closed"},
  637. },
  638. Callbacks{},
  639. )
  640. fmt.Println(fsm.Current())
  641. // Output: closed
  642. }
  643. func ExampleFSM_Is() {
  644. fsm := NewFSM(
  645. "closed",
  646. Events{
  647. {Name: "open", Src: []string{"closed"}, Dst: "open"},
  648. {Name: "close", Src: []string{"open"}, Dst: "closed"},
  649. },
  650. Callbacks{},
  651. )
  652. fmt.Println(fsm.Is("closed"))
  653. fmt.Println(fsm.Is("open"))
  654. // Output:
  655. // true
  656. // false
  657. }
  658. func ExampleFSM_Can() {
  659. fsm := NewFSM(
  660. "closed",
  661. Events{
  662. {Name: "open", Src: []string{"closed"}, Dst: "open"},
  663. {Name: "close", Src: []string{"open"}, Dst: "closed"},
  664. },
  665. Callbacks{},
  666. )
  667. fmt.Println(fsm.Can("open"))
  668. fmt.Println(fsm.Can("close"))
  669. // Output:
  670. // true
  671. // false
  672. }
  673. func ExampleFSM_Cannot() {
  674. fsm := NewFSM(
  675. "closed",
  676. Events{
  677. {Name: "open", Src: []string{"closed"}, Dst: "open"},
  678. {Name: "close", Src: []string{"open"}, Dst: "closed"},
  679. },
  680. Callbacks{},
  681. )
  682. fmt.Println(fsm.Cannot("open"))
  683. fmt.Println(fsm.Cannot("close"))
  684. // Output:
  685. // false
  686. // true
  687. }
  688. func ExampleFSM_Event() {
  689. fsm := NewFSM(
  690. "closed",
  691. Events{
  692. {Name: "open", Src: []string{"closed"}, Dst: "open"},
  693. {Name: "close", Src: []string{"open"}, Dst: "closed"},
  694. },
  695. Callbacks{},
  696. )
  697. fmt.Println(fsm.Current())
  698. err := fsm.Event("open")
  699. if err != nil {
  700. fmt.Println(err)
  701. }
  702. fmt.Println(fsm.Current())
  703. err = fsm.Event("close")
  704. if err != nil {
  705. fmt.Println(err)
  706. }
  707. fmt.Println(fsm.Current())
  708. // Output:
  709. // closed
  710. // open
  711. // closed
  712. }
  713. func ExampleFSM_Transition() {
  714. fsm := NewFSM(
  715. "closed",
  716. Events{
  717. {Name: "open", Src: []string{"closed"}, Dst: "open"},
  718. {Name: "close", Src: []string{"open"}, Dst: "closed"},
  719. },
  720. Callbacks{
  721. "leave_closed": func(e *Event) {
  722. e.Async()
  723. },
  724. },
  725. )
  726. err := fsm.Event("open")
  727. if e, ok := err.(AsyncError); !ok && e.Err != nil {
  728. fmt.Println(err)
  729. }
  730. fmt.Println(fsm.Current())
  731. err = fsm.Transition()
  732. if err != nil {
  733. fmt.Println(err)
  734. }
  735. fmt.Println(fsm.Current())
  736. // Output:
  737. // closed
  738. // open
  739. }