cgl_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. /*
  2. Tideland Common Go Library - Unit Tests
  3. Copyright (C) 2009-2011 Frank Mueller / Oldenburg / Germany
  4. Redistribution and use in source and binary forms, with or
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. Redistributions of source code must retain the above copyright notice, this
  8. list of conditions and the following disclaimer.
  9. Redistributions in binary form must reproduce the above copyright notice,
  10. this list of conditions and the following disclaimer in the documentation
  11. and/or other materials provided with the distribution.
  12. Neither the name of Tideland nor the names of its contributors may be
  13. used to endorse or promote products derived from this software without
  14. specific prior written permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  19. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  21. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  22. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  23. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  25. THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. package cgl
  28. //--------------------
  29. // IMPORTS
  30. //--------------------
  31. import (
  32. "bytes"
  33. "fmt"
  34. "log"
  35. "rand"
  36. "sort"
  37. "strconv"
  38. "strings"
  39. "testing"
  40. "time"
  41. )
  42. //--------------------
  43. // TESTS
  44. //--------------------
  45. // Test single recovering.
  46. func TestSingleRecovering(t *testing.T) {
  47. s := NewSupervisor(nil)
  48. ra := NewRecoverableAction(s)
  49. pos := ra.Action(PositiveAction)
  50. if pos == "OK" {
  51. t.Logf("Single recovering (a) OK.")
  52. } else {
  53. t.Errorf("Single recovering (a) failed! Reply is '%v'.", pos)
  54. }
  55. neg := ra.Action(FailAction)
  56. if neg == "Recovered" {
  57. t.Logf("Single recovering (b) OK.")
  58. } else {
  59. t.Errorf("Single recovering (b) failed! Reply is '%v.", neg)
  60. }
  61. }
  62. // Test multiple recovering.
  63. func TestMultipleRecovering(t *testing.T) {
  64. s := NewSupervisor(nil)
  65. raa := NewRecoverableAction(s)
  66. rab := NewRecoverableAction(s)
  67. rac := NewRecoverableAction(s)
  68. s.AddRecoverable("A", raa)
  69. s.AddRecoverable("B", rab)
  70. s.AddRecoverable("C", rac)
  71. t.Logf("(A) is '%v'.", raa.Action(FailAction))
  72. t.Logf("(B) is '%v'.", rab.Action(PositiveAction))
  73. t.Logf("(C) is '%v'.", rac.Action(PositiveAction))
  74. }
  75. // Test single heartbeat timeout.
  76. func TestSingleHeartbeatTimeout(t *testing.T) {
  77. ra := NewRecoverableAction(nil)
  78. reply := ra.Action(TimeConsumingAction)
  79. if reply == "Recovered" {
  80. t.Logf("Heartbeat timeout recovering OK.")
  81. } else {
  82. t.Errorf("Heartbeat timeout recovering failed! Reply is '%v.", reply)
  83. }
  84. }
  85. // Test multiple heartbeat timeout.
  86. func TestMultipleHeartbeatTimeout(t *testing.T) {
  87. s := NewSupervisor(nil)
  88. raa := NewRecoverableAction(s)
  89. rab := NewRecoverableAction(s)
  90. rac := NewRecoverableAction(s)
  91. s.AddRecoverable("A", raa)
  92. s.AddRecoverable("B", rab)
  93. s.AddRecoverable("C", rac)
  94. t.Logf("(A) is '%v'.", raa.Action(TimeConsumingAction))
  95. t.Logf("(B) is '%v'.", rab.Action(PositiveAction))
  96. t.Logf("(C) is '%v'.", rac.Action(PositiveAction))
  97. }
  98. // Test the finite state machine successfully.
  99. func TestFsmSuccess(t *testing.T) {
  100. fsm := NewFSM(NewLoginHandler(), -1)
  101. fsm.Send(&LoginPayload{"yadda"})
  102. fsm.Send(&PreparePayload{"foo", "bar"})
  103. fsm.Send(&LoginPayload{"yaddaA"})
  104. fsm.Send(&LoginPayload{"yaddaB"})
  105. fsm.Send(&LoginPayload{"yaddaC"})
  106. fsm.Send(&LoginPayload{"yaddaD"})
  107. fsm.Send(&UnlockPayload{})
  108. fsm.Send(&LoginPayload{"bar"})
  109. time.Sleep(1e7)
  110. t.Logf("Status: '%v'.", fsm.State())
  111. }
  112. // Test the finite state machine with timeout.
  113. func TestFsmTimeout(t *testing.T) {
  114. fsm := NewFSM(NewLoginHandler(), 1e5)
  115. fsm.Send(&LoginPayload{"yadda"})
  116. fsm.Send(&PreparePayload{"foo", "bar"})
  117. fsm.Send(&LoginPayload{"yaddaA"})
  118. fsm.Send(&LoginPayload{"yaddaB"})
  119. time.Sleep(1e8)
  120. fsm.Send(&LoginPayload{"yaddaC"})
  121. fsm.Send(&LoginPayload{"yaddaD"})
  122. fsm.Send(&UnlockPayload{})
  123. fsm.Send(&LoginPayload{"bar"})
  124. time.Sleep(1e7)
  125. t.Logf("Status: '%v'.", fsm.State())
  126. }
  127. // Test dispatching.
  128. func TestDispatching(t *testing.T) {
  129. tt := new(TT)
  130. v1, ok1 := Dispatch(tt, "Add", 4, 5)
  131. v2, ok2 := Dispatch(tt, "Add", 4, 5, 6)
  132. v3, ok3 := Dispatch(tt, "Mul", 4, 5, 6)
  133. v4, ok4 := Dispatch(tt, "Mul", 4, 5, 6, 7, 8)
  134. t.Logf("Add 1: %v / %v\n", v1, ok1)
  135. t.Logf("Add 2: %v / %v\n", v2, ok2)
  136. t.Logf("Mul 1: %v / %v\n", v3, ok3)
  137. t.Logf("Mul 2: %v / %v\n", v4, ok4)
  138. }
  139. // Test debug statement.
  140. func TestDebug(t *testing.T) {
  141. Debug("Hello, I'm debugging %v!", "here")
  142. }
  143. // Test nanoseconds calculation.
  144. func TestNanoseconds(t *testing.T) {
  145. t.Logf("Microseconds: %v\n", NsMicroseconds(4711))
  146. t.Logf("Milliseconds: %v\n", NsMilliseconds(4711))
  147. t.Logf("Seconds : %v\n", NsSeconds(4711))
  148. t.Logf("Minutes : %v\n", NsMinutes(4711))
  149. t.Logf("Hours : %v\n", NsHours(4711))
  150. t.Logf("Days : %v\n", NsDays(4711))
  151. t.Logf("Weeks : %v\n", NsWeeks(4711))
  152. }
  153. // Test time containments.
  154. func TestTimeContainments(t *testing.T) {
  155. now := time.UTC()
  156. years := []int64{2008, 2009, 2010}
  157. months := []int{3, 6, 9, 12}
  158. days := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  159. hours := []int{20, 21, 22, 23}
  160. minutes := []int{5, 10, 15, 20, 25, 30}
  161. seconds := []int{0, 15, 30, 45}
  162. weekdays := []int{time.Saturday, time.Sunday}
  163. t.Logf("Time is %s\n", now.Format(time.RFC822))
  164. t.Logf("Year in list : %t\n", YearInList(now, years))
  165. t.Logf("Year in range : %t\n", YearInRange(now, 2000, 2005))
  166. t.Logf("Month in list : %t\n", MonthInList(now, months))
  167. t.Logf("Month in range : %t\n", MonthInRange(now, 1, 6))
  168. t.Logf("Day in list : %t\n", DayInList(now, days))
  169. t.Logf("Day in range : %t\n", DayInRange(now, 15, 25))
  170. t.Logf("Hour in list : %t\n", HourInList(now, hours))
  171. t.Logf("Hour in range : %t\n", HourInRange(now, 9, 17))
  172. t.Logf("Minute in list : %t\n", MinuteInList(now, minutes))
  173. t.Logf("Minute in range : %t\n", MinuteInRange(now, 0, 29))
  174. t.Logf("Second in list : %t\n", SecondInList(now, seconds))
  175. t.Logf("Second in range : %t\n", SecondInRange(now, 30, 59))
  176. t.Logf("Weekday in list : %t\n", WeekdayInList(now, weekdays))
  177. t.Logf("Weekday in range: %t\n", WeekdayInRange(now, time.Monday, time.Friday))
  178. }
  179. // Test the UUID.
  180. func TestUuid(t *testing.T) {
  181. uuids := make(map[string]bool)
  182. t.Logf("Start generating UUIDs ...")
  183. for i := 0; i < 1000000; i++ {
  184. uuid := NewUUID().String()
  185. if uuids[uuid] {
  186. t.Fatalf("UUID collision")
  187. }
  188. uuids[uuid] = true
  189. }
  190. t.Logf("Done generating UUIDs!")
  191. }
  192. // Test the creation of an identifier.
  193. func TestIdentifier(t *testing.T) {
  194. // Type as identifier.
  195. var kvlf KeyValueLessFunc
  196. idp := TypeAsIdentifierPart(kvlf)
  197. if idp != "key-value-less-func" {
  198. t.Errorf("Identifier part for KeyValueLessFunc is wrong, returned '%v'!", idp)
  199. }
  200. idp = TypeAsIdentifierPart(NewUUID())
  201. if idp != "u-u-i-d" {
  202. t.Errorf("Identifier part for UUID is wrong, returned '%v'!", idp)
  203. }
  204. // Identifier.
  205. id := Identifier("One", 2, "three four")
  206. if id != "one:2:three-four" {
  207. t.Errorf("First identifier is wrong! Id: %v", id)
  208. }
  209. id = Identifier(2011, 6, 22, "One, two, or three things.")
  210. if id != "2011:6:22:one-two-or-three-things" {
  211. t.Errorf("Second identifier is wrong! Id: %v", id)
  212. }
  213. id = SepIdentifier("+", 1, "oNe", 2, "TWO", "3", "ÄÖÜ")
  214. if id != "1+one+2+two+3+äöü" {
  215. t.Errorf("Third identifier is wrong! Id: %v", id)
  216. }
  217. id = LimitedSepIdentifier("+", true, " ", 1, "oNe", 2, "TWO", "3", "ÄÖÜ", "Four", "+#-:,")
  218. if id != "1+one+2+two+3+four" {
  219. t.Errorf("Fourth identifier is wrong! Id: %v", id)
  220. }
  221. }
  222. // Test the integer generator.
  223. func TestLazyIntEvaluator(t *testing.T) {
  224. fibFunc := func(s interface{}) (interface{}, interface{}) {
  225. os := s.([]int)
  226. v1 := os[0]
  227. v2 := os[1]
  228. ns := []int{v2, v1 + v2}
  229. return v1, ns
  230. }
  231. fib := BuildLazyIntEvaluator(fibFunc, []int{0, 1})
  232. var fibs [25]int
  233. for i := 0; i < 25; i++ {
  234. fibs[i] = fib()
  235. }
  236. t.Logf("FIBS: %v", fibs)
  237. }
  238. // Test pivot.
  239. func TestPivot(t *testing.T) {
  240. a := make(sort.IntSlice, 15)
  241. for i := 0; i < len(a); i++ {
  242. a[i] = rand.Intn(99)
  243. }
  244. plo, phi := partition(a, 0, len(a)-1)
  245. t.Logf("PLO : %v", plo)
  246. t.Logf("PHI : %v", phi)
  247. t.Logf("PDATA: %v", a[phi-1])
  248. t.Logf("PIVOT: %v", a)
  249. }
  250. // Test sort shootout.
  251. func TestSort(t *testing.T) {
  252. ola := generateTestOrdersList(25000)
  253. olb := generateTestOrdersList(25000)
  254. olc := generateTestOrdersList(25000)
  255. old := generateTestOrdersList(25000)
  256. ta := time.Nanoseconds()
  257. Sort(ola)
  258. tb := time.Nanoseconds()
  259. sort.Sort(olb)
  260. tc := time.Nanoseconds()
  261. insertionSort(olc, 0, len(olc)-1)
  262. td := time.Nanoseconds()
  263. sequentialQuickSort(old, 0, len(olc)-1)
  264. te := time.Nanoseconds()
  265. t.Logf("PQS: %v", tb-ta)
  266. t.Logf(" QS: %v", tc-tb)
  267. t.Logf(" IS: %v", td-tc)
  268. t.Logf("SQS: %v", te-td)
  269. }
  270. // Test the parallel quicksort function.
  271. func TestParallelQuickSort(t *testing.T) {
  272. ol := generateTestOrdersList(10000)
  273. Sort(ol)
  274. cn := 0
  275. for _, o := range ol {
  276. if cn > o.CustomerNo {
  277. t.Errorf("Customer No %v in wrong order!", o.CustomerNo)
  278. cn = o.CustomerNo
  279. } else {
  280. cn = o.CustomerNo
  281. }
  282. }
  283. }
  284. // Test the MapReduce function.
  285. func TestMapReduce(t *testing.T) {
  286. // Start data producer.
  287. orderChan := generateTestOrders(2000)
  288. // Define map and reduce functions.
  289. mapFunc := func(in *KeyValue, mapEmitChan KeyValueChan) {
  290. o := in.Value.(*Order)
  291. // Emit analysis data for each item.
  292. for _, i := range o.Items {
  293. unitDiscount := (i.UnitPrice / 100.0) * i.DiscountPerc
  294. totalDiscount := unitDiscount * float64(i.Count)
  295. totalAmount := (i.UnitPrice - unitDiscount) * float64(i.Count)
  296. analysis := &OrderItemAnalysis{i.ArticleNo, i.Count, totalAmount, totalDiscount}
  297. articleNo := strconv.Itoa(i.ArticleNo)
  298. mapEmitChan <- &KeyValue{articleNo, analysis}
  299. }
  300. }
  301. reduceFunc := func(inChan KeyValueChan, reduceEmitChan KeyValueChan) {
  302. memory := make(map[string]*OrderItemAnalysis)
  303. // Collect emitted analysis data.
  304. for kv := range inChan {
  305. analysis := kv.Value.(*OrderItemAnalysis)
  306. if existing, ok := memory[kv.Key]; ok {
  307. existing.Quantity += analysis.Quantity
  308. existing.Amount += analysis.Amount
  309. existing.Discount += analysis.Discount
  310. } else {
  311. memory[kv.Key] = analysis
  312. }
  313. }
  314. // Emit it to map/reduce caller.
  315. for articleNo, analysis := range memory {
  316. reduceEmitChan <- &KeyValue{articleNo, analysis}
  317. }
  318. }
  319. // Now call MapReduce.
  320. for result := range SortedMapReduce(orderChan, mapFunc, 100, reduceFunc, 20, KeyLessFunc) {
  321. t.Logf("%v\n", result.Value)
  322. }
  323. }
  324. // Test job.
  325. func TestJob(t *testing.T) {
  326. // Check function.
  327. cf := func(now *time.Time) (perform, delete bool) {
  328. perform = now.Day == 1 &&
  329. now.Hour == 22 &&
  330. now.Minute == 0 &&
  331. SecondInList(now, []int{0, 10, 20, 30, 40, 50})
  332. delete = false
  333. return perform, delete
  334. }
  335. // Task function.
  336. tf := func(id string) { t.Logf("Performed job %s\n", id) }
  337. // Job and time.
  338. job := NewJob("test-job-a", cf, tf)
  339. time := time.LocalTime()
  340. // Test with non-matching time.
  341. time.Second = 1
  342. job.checkAndPerform(time)
  343. // Test with matching time
  344. time.Day = 1
  345. time.Hour = 22
  346. time.Minute = 0
  347. time.Second = 0
  348. job.checkAndPerform(time)
  349. }
  350. // Test crontab keeping the job.
  351. func TestCrontabKeep(t *testing.T) {
  352. ctb := NewCrontab()
  353. job := createJob(t, "keep", false)
  354. ctb.AddJob(job)
  355. time.Sleep(10 * 1e9)
  356. ctb.Stop()
  357. }
  358. // Test crontab deleting the job.
  359. func TestCrontabDelete(t *testing.T) {
  360. ctb := NewCrontab()
  361. job := createJob(t, "delete", true)
  362. ctb.AddJob(job)
  363. time.Sleep(10 * 1e9)
  364. ctb.Stop()
  365. }
  366. // Test creating.
  367. func TestSmlCreating(t *testing.T) {
  368. root := createSmlStructure()
  369. t.Logf("Root: %v", root)
  370. }
  371. // Test SML writer processing.
  372. func TestSmlWriterProcessing(t *testing.T) {
  373. root := createSmlStructure()
  374. bufA := bytes.NewBufferString("")
  375. bufB := bytes.NewBufferString("")
  376. sppA := NewSmlWriterProcessor(bufA, true)
  377. sppB := NewSmlWriterProcessor(bufB, false)
  378. root.ProcessWith(sppA)
  379. root.ProcessWith(sppB)
  380. t.Logf("Print A: %v", bufA)
  381. t.Logf("Print B: %v", bufB)
  382. }
  383. // Test positive reading.
  384. func TestSmlPositiveReading(t *testing.T) {
  385. sml := "Before! {foo {bar:1:first Yadda ^{Test^} 1} {inbetween} {bar:2:last Yadda {Test ^^} 2}} After!"
  386. reader := NewSmlReader(strings.NewReader(sml))
  387. root, err := reader.RootTagNode()
  388. if err == nil {
  389. t.Logf("Root:%v", root)
  390. } else {
  391. t.Errorf("Error: %v", err)
  392. }
  393. }
  394. // Test negative reading.
  395. func TestSmlNegativeReading(t *testing.T) {
  396. sml := "{Foo {bar:1 Yadda {test} {} 1} {bar:2 Yadda 2}}"
  397. reader := NewSmlReader(strings.NewReader(sml))
  398. root, err := reader.RootTagNode()
  399. if err == nil {
  400. t.Errorf("Root: %v", root)
  401. } else {
  402. t.Logf("Error: %v", err)
  403. }
  404. }
  405. // Test of the ETM monitor.
  406. func TestEtmMonitor(t *testing.T) {
  407. mon := Monitor()
  408. // Generate measurings.
  409. for i := 0; i < 500; i++ {
  410. n := rand.Intn(10)
  411. id := fmt.Sprintf("Work %d", n)
  412. m := mon.BeginMeasuring(id)
  413. work(n * 5000)
  414. m.EndMeasuring()
  415. }
  416. // Print, process with error, and print again.
  417. mon.MeasuringPointsPrintAll()
  418. mon.MeasuringPointsDo(func(mp *MeasuringPoint) {
  419. if mp.Count >= 25 {
  420. // Divide by zero.
  421. mp.Count = mp.Count / (mp.Count - mp.Count)
  422. }
  423. })
  424. mon.MeasuringPointsPrintAll()
  425. }
  426. // Test of the SSI monitor.
  427. func TestSsiMonitor(t *testing.T) {
  428. mon := Monitor()
  429. // Generate values.
  430. for i := 0; i < 500; i++ {
  431. n := rand.Intn(10)
  432. id := fmt.Sprintf("Work %d", n)
  433. mon.SetValue(id, rand.Int63n(2001)-1000)
  434. }
  435. // Print, process with error, and print again.
  436. mon.StaySetVariablesPrintAll()
  437. mon.StaySetVariablesDo(func(ssv *StaySetVariable) {
  438. if ssv.Count >= 25 {
  439. // Divide by zero.
  440. ssv.Count = ssv.Count / (ssv.Count - ssv.Count)
  441. }
  442. })
  443. mon.StaySetVariablesPrintAll()
  444. }
  445. // Test of the DSR monitor.
  446. func TestDsrMonitor(t *testing.T) {
  447. mon := Monitor()
  448. mon.Register("monitor:dsr:a", func() string { return "A" })
  449. mon.Register("monitor:dsr:b", func() string { return "4711" })
  450. mon.Register("monitor:dsr:c", func() string { return "2011-05-07" })
  451. mon.DynamicStatusValuesPrintAll()
  452. }
  453. //--------------------
  454. // HELPERS
  455. //--------------------
  456. // Test type.
  457. type TT struct{}
  458. func (tt *TT) Add(a, b int) int { return a + b }
  459. func (tt *TT) Mul(a, b, c, d, e int) int { return a * b * c * d * e }
  460. // Order item type.
  461. type OrderItem struct {
  462. ArticleNo int
  463. Count int
  464. UnitPrice float64
  465. DiscountPerc float64
  466. }
  467. // Order type.
  468. type Order struct {
  469. OrderNo UUID
  470. CustomerNo int
  471. Items []*OrderItem
  472. }
  473. func (o *Order) String() string {
  474. msg := "ON: %v / CN: %4v / I: %v"
  475. return fmt.Sprintf(msg, o.OrderNo, o.CustomerNo, len(o.Items))
  476. }
  477. // Order item analysis type.
  478. type OrderItemAnalysis struct {
  479. ArticleNo int
  480. Quantity int
  481. Amount float64
  482. Discount float64
  483. }
  484. func (oia *OrderItemAnalysis) String() string {
  485. msg := "AN: %5v / Q: %4v / A: %10.2f € / D: %10.2f €"
  486. return fmt.Sprintf(msg, oia.ArticleNo, oia.Quantity, oia.Amount, oia.Discount)
  487. }
  488. // Order list.
  489. type OrderList []*Order
  490. func (l OrderList) Len() int {
  491. return len(l)
  492. }
  493. func (l OrderList) Less(i, j int) bool {
  494. return l[i].CustomerNo < l[j].CustomerNo
  495. }
  496. func (l OrderList) Swap(i, j int) {
  497. l[i], l[j] = l[j], l[i]
  498. }
  499. // Action function.
  500. type Action func() string
  501. // A positive action.
  502. func PositiveAction() string {
  503. log.Printf("Perform positive action.")
  504. return "OK"
  505. }
  506. // A failing action.
  507. func FailAction() string {
  508. log.Printf("Perform failing action!")
  509. panic("Fail!")
  510. return "Fail!"
  511. }
  512. // A time consuming action.
  513. func TimeConsumingAction() string {
  514. log.Printf("Perform time consuming action!")
  515. time.Sleep(3e8)
  516. return "Time consumed"
  517. }
  518. // Recoverable action type.
  519. type RecoverableAction struct {
  520. actionChan chan Action
  521. replyChan chan string
  522. supervisor *Supervisor
  523. heartbeat *Heartbeat
  524. }
  525. // Create a new recoverable action.
  526. func NewRecoverableAction(supervisor *Supervisor) *RecoverableAction {
  527. ra := &RecoverableAction{
  528. actionChan: make(chan Action),
  529. replyChan: make(chan string, 5),
  530. supervisor: supervisor,
  531. }
  532. ra.heartbeat = NewHeartbeat(ra, 1e8)
  533. go ra.backend()
  534. return ra
  535. }
  536. // Send an action to perform.
  537. func (ra *RecoverableAction) Action(action Action) string {
  538. ra.actionChan <- action
  539. return <-ra.replyChan
  540. }
  541. // Implement Supervisor() of the recoverable interface.
  542. func (ra *RecoverableAction) Supervisor() *Supervisor {
  543. return ra.supervisor
  544. }
  545. // Implement Recover() of the recoverable interface.
  546. func (ra *RecoverableAction) Recover(r Recoverable, err interface{}) {
  547. if ra == r {
  548. log.Printf("Recovering error '%v'!", err)
  549. ra.replyChan <- "Recovered"
  550. go ra.backend()
  551. }
  552. }
  553. // Backend of the recoverable action.
  554. func (ra *RecoverableAction) backend() {
  555. defer func() {
  556. HelpIfNeeded(ra, recover())
  557. }()
  558. for {
  559. select {
  560. case action := <-ra.actionChan:
  561. ra.replyChan <- action()
  562. case h := <-ra.heartbeat.HeartbeatChan:
  563. ImAlive(h)
  564. }
  565. }
  566. }
  567. // Generate a test order.
  568. func generateTestOrders(count int) KeyValueChan {
  569. articleMaxNo := 9999
  570. unitPrices := make([]float64, articleMaxNo+1)
  571. for i := 0; i < articleMaxNo+1; i++ {
  572. unitPrices[i] = rand.Float64() * 100.0
  573. }
  574. kvc := make(KeyValueChan)
  575. go func() {
  576. for i := 0; i < count; i++ {
  577. order := new(Order)
  578. order.OrderNo = NewUUID()
  579. order.CustomerNo = rand.Intn(999) + 1
  580. order.Items = make([]*OrderItem, rand.Intn(9)+1)
  581. for j := 0; j < len(order.Items); j++ {
  582. articleNo := rand.Intn(articleMaxNo)
  583. order.Items[j] = &OrderItem{
  584. ArticleNo: articleNo,
  585. Count: rand.Intn(9) + 1,
  586. UnitPrice: unitPrices[articleNo],
  587. DiscountPerc: rand.Float64() * 4.0,
  588. }
  589. }
  590. kvc <- &KeyValue{order.OrderNo.String(), order}
  591. }
  592. close(kvc)
  593. }()
  594. return kvc
  595. }
  596. // Generate a list with test orders.
  597. func generateTestOrdersList(count int) OrderList {
  598. l := make(OrderList, count)
  599. idx := 0
  600. for kv := range generateTestOrders(count) {
  601. l[idx] = kv.Value.(*Order)
  602. idx++
  603. }
  604. return l
  605. }
  606. // Create a job that leads to a an event every 2 seconds.
  607. func createJob(t *testing.T, descr string, delete bool) *Job {
  608. cf := func(now *time.Time) (bool, bool) { return now.Seconds()%2 == 0, delete }
  609. tf := func(id string) { t.Logf("Performed job %s\n", id) }
  610. return NewJob("test-server-"+descr, cf, tf)
  611. }
  612. // Create a SML structure.
  613. func createSmlStructure() *TagNode {
  614. root := NewTagNode("root")
  615. root.AppendText("Text A")
  616. root.AppendText("Text B")
  617. root.AppendTaggedText("comment", "A first comment.")
  618. subA := root.AppendTag("sub-a:1st:important")
  619. subA.AppendText("Text A.A")
  620. root.AppendTaggedText("comment", "A second comment.")
  621. subB := root.AppendTag("sub-b:2nd")
  622. subB.AppendText("Text B.A")
  623. subB.AppendTaggedText("raw", "Any raw text with {, }, and ^.")
  624. return root
  625. }
  626. // Do some work.
  627. func work(n int) int {
  628. if n < 0 {
  629. return 0
  630. }
  631. return n * work(n-1)
  632. }
  633. //--------------------
  634. // TEST LOGIN EVENT HANDLER
  635. //--------------------
  636. // Prepare payload.
  637. type PreparePayload struct {
  638. userId string
  639. password string
  640. }
  641. // Login payload.
  642. type LoginPayload struct {
  643. password string
  644. }
  645. // Reset payload.
  646. type ResetPayload struct{}
  647. // Unlock payload.
  648. type UnlockPayload struct{}
  649. // Login handler tyoe.
  650. type LoginHandler struct {
  651. userId string
  652. password string
  653. illegalLoginCounter int
  654. locked bool
  655. }
  656. // Create a new login handler.
  657. func NewLoginHandler() *LoginHandler {
  658. return new(LoginHandler)
  659. }
  660. // Return the initial state.
  661. func (lh *LoginHandler) Init() string {
  662. return "New"
  663. }
  664. // Terminate the handler.
  665. func (lh *LoginHandler) Terminate(string, interface{}) string {
  666. return "LoggedIn"
  667. }
  668. // Handler for state: "New".
  669. func (lh *LoginHandler) HandleStateNew(c *Condition) (string, interface{}) {
  670. switch pld := c.Payload.(type) {
  671. case *PreparePayload:
  672. lh.userId = pld.userId
  673. lh.password = pld.password
  674. lh.illegalLoginCounter = 0
  675. lh.locked = false
  676. log.Printf("User '%v' prepared.", lh.userId)
  677. return "Authenticating", nil
  678. case *LoginPayload:
  679. log.Printf("Illegal login, handler not initialized!")
  680. return "New", false
  681. case Timeout:
  682. log.Printf("Timeout, terminate handler!")
  683. return "Terminate", nil
  684. }
  685. log.Printf("Illegal payload '%v' during state 'new'!", c.Payload)
  686. return "New", nil
  687. }
  688. // Handler for state: "Authenticating".
  689. func (lh *LoginHandler) HandleStateAuthenticating(c *Condition) (string, interface{}) {
  690. switch pld := c.Payload.(type) {
  691. case *LoginPayload:
  692. if pld.password == lh.password {
  693. lh.illegalLoginCounter = 0
  694. lh.locked = false
  695. log.Printf("User '%v' logged in.", lh.userId)
  696. return "Terminate", true
  697. }
  698. log.Printf("User '%v' used illegal password.", lh.userId)
  699. lh.illegalLoginCounter++
  700. if lh.illegalLoginCounter == 3 {
  701. lh.locked = true
  702. log.Printf("User '%v' locked!", lh.userId)
  703. return "Locked", false
  704. }
  705. return "Authenticating", false
  706. case *UnlockPayload:
  707. log.Printf("No need to unlock user '%v'!", lh.userId)
  708. return "Authenticating", nil
  709. case *ResetPayload, Timeout:
  710. lh.illegalLoginCounter = 0
  711. lh.locked = false
  712. log.Printf("User '%v' resetted.", lh.userId)
  713. return "Authenticating", nil
  714. }
  715. log.Printf("Illegal payload '%v' during state 'authenticating'!", c.Payload)
  716. return "Authenticating", nil
  717. }
  718. // Handler for state: "Locked".
  719. func (lh *LoginHandler) HandleStateLocked(c *Condition) (string, interface{}) {
  720. switch pld := c.Payload.(type) {
  721. case *LoginPayload:
  722. log.Printf("User '%v' login rejected, user is locked!", lh.userId)
  723. return "Locked", false
  724. case *ResetPayload, *UnlockPayload, Timeout:
  725. lh.illegalLoginCounter = 0
  726. lh.locked = false
  727. log.Printf("User '%v' resetted / unlocked.", lh.userId)
  728. return "Authenticating", nil
  729. }
  730. log.Printf("Illegal payload '%v' during state 'loacked'!", c.Payload)
  731. return "Locked", nil
  732. }
  733. /*
  734. EOF
  735. */