fsm_test.go 16 KB

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