| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972 |
- /*
- Tideland Common Go Library - Unit Tests
- Copyright (C) 2009-2011 Frank Mueller / Oldenburg / Germany
- Redistribution and use in source and binary forms, with or
- modification, are permitted provided that the following conditions are
- met:
- Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- Neither the name of Tideland nor the names of its contributors may be
- used to endorse or promote products derived from this software without
- specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- THE POSSIBILITY OF SUCH DAMAGE.
- */
- package cgl
- //--------------------
- // IMPORTS
- //--------------------
- import (
- "bytes"
- "fmt"
- "log"
- "rand"
- "sort"
- "strconv"
- "strings"
- "testing"
- "time"
- )
- //--------------------
- // TESTS
- //--------------------
- // Test single recovering.
- func TestSingleRecovering(t *testing.T) {
- s := NewSupervisor(nil)
- ra := NewRecoverableAction(s)
- pos := ra.Action(PositiveAction)
- if pos == "OK" {
- t.Logf("Single recovering (a) OK.")
- } else {
- t.Errorf("Single recovering (a) failed! Reply is '%v'.", pos)
- }
- neg := ra.Action(FailAction)
- if neg == "Recovered" {
- t.Logf("Single recovering (b) OK.")
- } else {
- t.Errorf("Single recovering (b) failed! Reply is '%v.", neg)
- }
- }
- // Test multiple recovering.
- func TestMultipleRecovering(t *testing.T) {
- s := NewSupervisor(nil)
- raa := NewRecoverableAction(s)
- rab := NewRecoverableAction(s)
- rac := NewRecoverableAction(s)
- s.AddRecoverable("A", raa)
- s.AddRecoverable("B", rab)
- s.AddRecoverable("C", rac)
- t.Logf("(A) is '%v'.", raa.Action(FailAction))
- t.Logf("(B) is '%v'.", rab.Action(PositiveAction))
- t.Logf("(C) is '%v'.", rac.Action(PositiveAction))
- }
- // Test single heartbeat timeout.
- func TestSingleHeartbeatTimeout(t *testing.T) {
- ra := NewRecoverableAction(nil)
- reply := ra.Action(TimeConsumingAction)
- if reply == "Recovered" {
- t.Logf("Heartbeat timeout recovering OK.")
- } else {
- t.Errorf("Heartbeat timeout recovering failed! Reply is '%v.", reply)
- }
- }
- // Test multiple heartbeat timeout.
- func TestMultipleHeartbeatTimeout(t *testing.T) {
- s := NewSupervisor(nil)
- raa := NewRecoverableAction(s)
- rab := NewRecoverableAction(s)
- rac := NewRecoverableAction(s)
- s.AddRecoverable("A", raa)
- s.AddRecoverable("B", rab)
- s.AddRecoverable("C", rac)
- t.Logf("(A) is '%v'.", raa.Action(TimeConsumingAction))
- t.Logf("(B) is '%v'.", rab.Action(PositiveAction))
- t.Logf("(C) is '%v'.", rac.Action(PositiveAction))
- }
- // Test the finite state machine successfully.
- func TestFsmSuccess(t *testing.T) {
- fsm := NewFSM(NewLoginHandler(), -1)
- fsm.Send(&LoginPayload{"yadda"})
- fsm.Send(&PreparePayload{"foo", "bar"})
- fsm.Send(&LoginPayload{"yaddaA"})
- fsm.Send(&LoginPayload{"yaddaB"})
- fsm.Send(&LoginPayload{"yaddaC"})
- fsm.Send(&LoginPayload{"yaddaD"})
- fsm.Send(&UnlockPayload{})
- fsm.Send(&LoginPayload{"bar"})
- time.Sleep(1e7)
- t.Logf("Status: '%v'.", fsm.State())
- }
- // Test the finite state machine with timeout.
- func TestFsmTimeout(t *testing.T) {
- fsm := NewFSM(NewLoginHandler(), 1e5)
- fsm.Send(&LoginPayload{"yadda"})
- fsm.Send(&PreparePayload{"foo", "bar"})
- fsm.Send(&LoginPayload{"yaddaA"})
- fsm.Send(&LoginPayload{"yaddaB"})
- time.Sleep(1e8)
- fsm.Send(&LoginPayload{"yaddaC"})
- fsm.Send(&LoginPayload{"yaddaD"})
- fsm.Send(&UnlockPayload{})
- fsm.Send(&LoginPayload{"bar"})
- time.Sleep(1e7)
- t.Logf("Status: '%v'.", fsm.State())
- }
- // Test dispatching.
- func TestDispatching(t *testing.T) {
- tt := new(TT)
- v1, ok1 := Dispatch(tt, "Add", 4, 5)
- v2, ok2 := Dispatch(tt, "Add", 4, 5, 6)
- v3, ok3 := Dispatch(tt, "Mul", 4, 5, 6)
- v4, ok4 := Dispatch(tt, "Mul", 4, 5, 6, 7, 8)
- t.Logf("Add 1: %v / %v\n", v1, ok1)
- t.Logf("Add 2: %v / %v\n", v2, ok2)
- t.Logf("Mul 1: %v / %v\n", v3, ok3)
- t.Logf("Mul 2: %v / %v\n", v4, ok4)
- }
- // Test debug statement.
- func TestDebug(t *testing.T) {
- Debug("Hello, I'm debugging %v!", "here")
- }
- // Test nanoseconds calculation.
- func TestNanoseconds(t *testing.T) {
- t.Logf("Microseconds: %v\n", NsMicroseconds(4711))
- t.Logf("Milliseconds: %v\n", NsMilliseconds(4711))
- t.Logf("Seconds : %v\n", NsSeconds(4711))
- t.Logf("Minutes : %v\n", NsMinutes(4711))
- t.Logf("Hours : %v\n", NsHours(4711))
- t.Logf("Days : %v\n", NsDays(4711))
- t.Logf("Weeks : %v\n", NsWeeks(4711))
- }
- // Test time containments.
- func TestTimeContainments(t *testing.T) {
- now := time.UTC()
- years := []int64{2008, 2009, 2010}
- months := []int{3, 6, 9, 12}
- days := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
- hours := []int{20, 21, 22, 23}
- minutes := []int{5, 10, 15, 20, 25, 30}
- seconds := []int{0, 15, 30, 45}
- weekdays := []int{time.Saturday, time.Sunday}
- t.Logf("Time is %s\n", now.Format(time.RFC822))
- t.Logf("Year in list : %t\n", YearInList(now, years))
- t.Logf("Year in range : %t\n", YearInRange(now, 2000, 2005))
- t.Logf("Month in list : %t\n", MonthInList(now, months))
- t.Logf("Month in range : %t\n", MonthInRange(now, 1, 6))
- t.Logf("Day in list : %t\n", DayInList(now, days))
- t.Logf("Day in range : %t\n", DayInRange(now, 15, 25))
- t.Logf("Hour in list : %t\n", HourInList(now, hours))
- t.Logf("Hour in range : %t\n", HourInRange(now, 9, 17))
- t.Logf("Minute in list : %t\n", MinuteInList(now, minutes))
- t.Logf("Minute in range : %t\n", MinuteInRange(now, 0, 29))
- t.Logf("Second in list : %t\n", SecondInList(now, seconds))
- t.Logf("Second in range : %t\n", SecondInRange(now, 30, 59))
- t.Logf("Weekday in list : %t\n", WeekdayInList(now, weekdays))
- t.Logf("Weekday in range: %t\n", WeekdayInRange(now, time.Monday, time.Friday))
- }
- // Test the UUID.
- func TestUuid(t *testing.T) {
- uuids := make(map[string]bool)
- t.Logf("Start generating UUIDs ...")
- for i := 0; i < 1000000; i++ {
- uuid := NewUUID().String()
- if uuids[uuid] {
- t.Fatalf("UUID collision")
- }
- uuids[uuid] = true
- }
- t.Logf("Done generating UUIDs!")
- }
- // Test the creation of an identifier.
- func TestIdentifier(t *testing.T) {
- // Type as identifier.
- var kvlf KeyValueLessFunc
- idp := TypeAsIdentifierPart(kvlf)
- if idp != "key-value-less-func" {
- t.Errorf("Identifier part for KeyValueLessFunc is wrong, returned '%v'!", idp)
- }
- idp = TypeAsIdentifierPart(NewUUID())
- if idp != "u-u-i-d" {
- t.Errorf("Identifier part for UUID is wrong, returned '%v'!", idp)
- }
- // Identifier.
- id := Identifier("One", 2, "three four")
- if id != "one:2:three-four" {
- t.Errorf("First identifier is wrong! Id: %v", id)
- }
- id = Identifier(2011, 6, 22, "One, two, or three things.")
- if id != "2011:6:22:one-two-or-three-things" {
- t.Errorf("Second identifier is wrong! Id: %v", id)
- }
- id = SepIdentifier("+", 1, "oNe", 2, "TWO", "3", "ÄÖÜ")
- if id != "1+one+2+two+3+äöü" {
- t.Errorf("Third identifier is wrong! Id: %v", id)
- }
- id = LimitedSepIdentifier("+", true, " ", 1, "oNe", 2, "TWO", "3", "ÄÖÜ", "Four", "+#-:,")
- if id != "1+one+2+two+3+four" {
- t.Errorf("Fourth identifier is wrong! Id: %v", id)
- }
- }
- // Test the integer generator.
- func TestLazyIntEvaluator(t *testing.T) {
- fibFunc := func(s interface{}) (interface{}, interface{}) {
- os := s.([]int)
- v1 := os[0]
- v2 := os[1]
- ns := []int{v2, v1 + v2}
- return v1, ns
- }
- fib := BuildLazyIntEvaluator(fibFunc, []int{0, 1})
- var fibs [25]int
- for i := 0; i < 25; i++ {
- fibs[i] = fib()
- }
- t.Logf("FIBS: %v", fibs)
- }
- // Test pivot.
- func TestPivot(t *testing.T) {
- a := make(sort.IntSlice, 15)
- for i := 0; i < len(a); i++ {
- a[i] = rand.Intn(99)
- }
- plo, phi := partition(a, 0, len(a)-1)
- t.Logf("PLO : %v", plo)
- t.Logf("PHI : %v", phi)
- t.Logf("PDATA: %v", a[phi-1])
- t.Logf("PIVOT: %v", a)
- }
- // Test sort shootout.
- func TestSort(t *testing.T) {
- ola := generateTestOrdersList(25000)
- olb := generateTestOrdersList(25000)
- olc := generateTestOrdersList(25000)
- old := generateTestOrdersList(25000)
- ta := time.Nanoseconds()
- Sort(ola)
- tb := time.Nanoseconds()
- sort.Sort(olb)
- tc := time.Nanoseconds()
- insertionSort(olc, 0, len(olc)-1)
- td := time.Nanoseconds()
- sequentialQuickSort(old, 0, len(olc)-1)
- te := time.Nanoseconds()
- t.Logf("PQS: %v", tb-ta)
- t.Logf(" QS: %v", tc-tb)
- t.Logf(" IS: %v", td-tc)
- t.Logf("SQS: %v", te-td)
- }
- // Test the parallel quicksort function.
- func TestParallelQuickSort(t *testing.T) {
- ol := generateTestOrdersList(10000)
- Sort(ol)
- cn := 0
- for _, o := range ol {
- if cn > o.CustomerNo {
- t.Errorf("Customer No %v in wrong order!", o.CustomerNo)
- cn = o.CustomerNo
- } else {
- cn = o.CustomerNo
- }
- }
- }
- // Test the MapReduce function.
- func TestMapReduce(t *testing.T) {
- // Start data producer.
- orderChan := generateTestOrders(2000)
- // Define map and reduce functions.
- mapFunc := func(in *KeyValue, mapEmitChan KeyValueChan) {
- o := in.Value.(*Order)
- // Emit analysis data for each item.
- for _, i := range o.Items {
- unitDiscount := (i.UnitPrice / 100.0) * i.DiscountPerc
- totalDiscount := unitDiscount * float64(i.Count)
- totalAmount := (i.UnitPrice - unitDiscount) * float64(i.Count)
- analysis := &OrderItemAnalysis{i.ArticleNo, i.Count, totalAmount, totalDiscount}
- articleNo := strconv.Itoa(i.ArticleNo)
- mapEmitChan <- &KeyValue{articleNo, analysis}
- }
- }
- reduceFunc := func(inChan KeyValueChan, reduceEmitChan KeyValueChan) {
- memory := make(map[string]*OrderItemAnalysis)
- // Collect emitted analysis data.
- for kv := range inChan {
- analysis := kv.Value.(*OrderItemAnalysis)
- if existing, ok := memory[kv.Key]; ok {
- existing.Quantity += analysis.Quantity
- existing.Amount += analysis.Amount
- existing.Discount += analysis.Discount
- } else {
- memory[kv.Key] = analysis
- }
- }
- // Emit it to map/reduce caller.
- for articleNo, analysis := range memory {
- reduceEmitChan <- &KeyValue{articleNo, analysis}
- }
- }
- // Now call MapReduce.
- for result := range SortedMapReduce(orderChan, mapFunc, 100, reduceFunc, 20, KeyLessFunc) {
- t.Logf("%v\n", result.Value)
- }
- }
- // Test job.
- func TestJob(t *testing.T) {
- // Check function.
- cf := func(now *time.Time) (perform, delete bool) {
- perform = now.Day == 1 &&
- now.Hour == 22 &&
- now.Minute == 0 &&
- SecondInList(now, []int{0, 10, 20, 30, 40, 50})
- delete = false
- return perform, delete
- }
- // Task function.
- tf := func(id string) { t.Logf("Performed job %s\n", id) }
- // Job and time.
- job := NewJob("test-job-a", cf, tf)
- time := time.LocalTime()
- // Test with non-matching time.
- time.Second = 1
- job.checkAndPerform(time)
- // Test with matching time
- time.Day = 1
- time.Hour = 22
- time.Minute = 0
- time.Second = 0
- job.checkAndPerform(time)
- }
- // Test crontab keeping the job.
- func TestCrontabKeep(t *testing.T) {
- ctb := NewCrontab()
- job := createJob(t, "keep", false)
- ctb.AddJob(job)
- time.Sleep(10 * 1e9)
- ctb.Stop()
- }
- // Test crontab deleting the job.
- func TestCrontabDelete(t *testing.T) {
- ctb := NewCrontab()
- job := createJob(t, "delete", true)
- ctb.AddJob(job)
- time.Sleep(10 * 1e9)
- ctb.Stop()
- }
- // Test creating.
- func TestSmlCreating(t *testing.T) {
- root := createSmlStructure()
- t.Logf("Root: %v", root)
- }
- // Test SML writer processing.
- func TestSmlWriterProcessing(t *testing.T) {
- root := createSmlStructure()
- bufA := bytes.NewBufferString("")
- bufB := bytes.NewBufferString("")
- sppA := NewSmlWriterProcessor(bufA, true)
- sppB := NewSmlWriterProcessor(bufB, false)
- root.ProcessWith(sppA)
- root.ProcessWith(sppB)
- t.Logf("Print A: %v", bufA)
- t.Logf("Print B: %v", bufB)
- }
- // Test positive reading.
- func TestSmlPositiveReading(t *testing.T) {
- sml := "Before! {foo {bar:1:first Yadda ^{Test^} 1} {inbetween} {bar:2:last Yadda {Test ^^} 2}} After!"
- reader := NewSmlReader(strings.NewReader(sml))
- root, err := reader.RootTagNode()
- if err == nil {
- t.Logf("Root:%v", root)
- } else {
- t.Errorf("Error: %v", err)
- }
- }
- // Test negative reading.
- func TestSmlNegativeReading(t *testing.T) {
- sml := "{Foo {bar:1 Yadda {test} {} 1} {bar:2 Yadda 2}}"
- reader := NewSmlReader(strings.NewReader(sml))
- root, err := reader.RootTagNode()
- if err == nil {
- t.Errorf("Root: %v", root)
- } else {
- t.Logf("Error: %v", err)
- }
- }
- // Test of the ETM monitor.
- func TestEtmMonitor(t *testing.T) {
- mon := Monitor()
- // Generate measurings.
- for i := 0; i < 500; i++ {
- n := rand.Intn(10)
- id := fmt.Sprintf("Work %d", n)
- m := mon.BeginMeasuring(id)
- work(n * 5000)
- m.EndMeasuring()
- }
- // Print, process with error, and print again.
- mon.MeasuringPointsPrintAll()
- mon.MeasuringPointsDo(func(mp *MeasuringPoint) {
- if mp.Count >= 25 {
- // Divide by zero.
- mp.Count = mp.Count / (mp.Count - mp.Count)
- }
- })
- mon.MeasuringPointsPrintAll()
- }
- // Test of the SSI monitor.
- func TestSsiMonitor(t *testing.T) {
- mon := Monitor()
- // Generate values.
- for i := 0; i < 500; i++ {
- n := rand.Intn(10)
- id := fmt.Sprintf("Work %d", n)
- mon.SetValue(id, rand.Int63n(2001)-1000)
- }
- // Print, process with error, and print again.
- mon.StaySetVariablesPrintAll()
- mon.StaySetVariablesDo(func(ssv *StaySetVariable) {
- if ssv.Count >= 25 {
- // Divide by zero.
- ssv.Count = ssv.Count / (ssv.Count - ssv.Count)
- }
- })
- mon.StaySetVariablesPrintAll()
- }
- // Test of the DSR monitor.
- func TestDsrMonitor(t *testing.T) {
- mon := Monitor()
- mon.Register("monitor:dsr:a", func() string { return "A" })
- mon.Register("monitor:dsr:b", func() string { return "4711" })
- mon.Register("monitor:dsr:c", func() string { return "2011-05-07" })
- mon.DynamicStatusValuesPrintAll()
- }
- //--------------------
- // HELPERS
- //--------------------
- // Test type.
- type TT struct{}
- func (tt *TT) Add(a, b int) int { return a + b }
- func (tt *TT) Mul(a, b, c, d, e int) int { return a * b * c * d * e }
- // Order item type.
- type OrderItem struct {
- ArticleNo int
- Count int
- UnitPrice float64
- DiscountPerc float64
- }
- // Order type.
- type Order struct {
- OrderNo UUID
- CustomerNo int
- Items []*OrderItem
- }
- func (o *Order) String() string {
- msg := "ON: %v / CN: %4v / I: %v"
- return fmt.Sprintf(msg, o.OrderNo, o.CustomerNo, len(o.Items))
- }
- // Order item analysis type.
- type OrderItemAnalysis struct {
- ArticleNo int
- Quantity int
- Amount float64
- Discount float64
- }
- func (oia *OrderItemAnalysis) String() string {
- msg := "AN: %5v / Q: %4v / A: %10.2f € / D: %10.2f €"
- return fmt.Sprintf(msg, oia.ArticleNo, oia.Quantity, oia.Amount, oia.Discount)
- }
- // Order list.
- type OrderList []*Order
- func (l OrderList) Len() int {
- return len(l)
- }
- func (l OrderList) Less(i, j int) bool {
- return l[i].CustomerNo < l[j].CustomerNo
- }
- func (l OrderList) Swap(i, j int) {
- l[i], l[j] = l[j], l[i]
- }
- // Action function.
- type Action func() string
- // A positive action.
- func PositiveAction() string {
- log.Printf("Perform positive action.")
- return "OK"
- }
- // A failing action.
- func FailAction() string {
- log.Printf("Perform failing action!")
- panic("Fail!")
- return "Fail!"
- }
- // A time consuming action.
- func TimeConsumingAction() string {
- log.Printf("Perform time consuming action!")
- time.Sleep(3e8)
- return "Time consumed"
- }
- // Recoverable action type.
- type RecoverableAction struct {
- actionChan chan Action
- replyChan chan string
- supervisor *Supervisor
- heartbeat *Heartbeat
- }
- // Create a new recoverable action.
- func NewRecoverableAction(supervisor *Supervisor) *RecoverableAction {
- ra := &RecoverableAction{
- actionChan: make(chan Action),
- replyChan: make(chan string, 5),
- supervisor: supervisor,
- }
- ra.heartbeat = NewHeartbeat(ra, 1e8)
- go ra.backend()
- return ra
- }
- // Send an action to perform.
- func (ra *RecoverableAction) Action(action Action) string {
- ra.actionChan <- action
- return <-ra.replyChan
- }
- // Implement Supervisor() of the recoverable interface.
- func (ra *RecoverableAction) Supervisor() *Supervisor {
- return ra.supervisor
- }
- // Implement Recover() of the recoverable interface.
- func (ra *RecoverableAction) Recover(r Recoverable, err interface{}) {
- if ra == r {
- log.Printf("Recovering error '%v'!", err)
- ra.replyChan <- "Recovered"
- go ra.backend()
- }
- }
- // Backend of the recoverable action.
- func (ra *RecoverableAction) backend() {
- defer func() {
- HelpIfNeeded(ra, recover())
- }()
- for {
- select {
- case action := <-ra.actionChan:
- ra.replyChan <- action()
- case h := <-ra.heartbeat.HeartbeatChan:
- ImAlive(h)
- }
- }
- }
- // Generate a test order.
- func generateTestOrders(count int) KeyValueChan {
- articleMaxNo := 9999
- unitPrices := make([]float64, articleMaxNo+1)
- for i := 0; i < articleMaxNo+1; i++ {
- unitPrices[i] = rand.Float64() * 100.0
- }
- kvc := make(KeyValueChan)
- go func() {
- for i := 0; i < count; i++ {
- order := new(Order)
- order.OrderNo = NewUUID()
- order.CustomerNo = rand.Intn(999) + 1
- order.Items = make([]*OrderItem, rand.Intn(9)+1)
- for j := 0; j < len(order.Items); j++ {
- articleNo := rand.Intn(articleMaxNo)
- order.Items[j] = &OrderItem{
- ArticleNo: articleNo,
- Count: rand.Intn(9) + 1,
- UnitPrice: unitPrices[articleNo],
- DiscountPerc: rand.Float64() * 4.0,
- }
- }
- kvc <- &KeyValue{order.OrderNo.String(), order}
- }
- close(kvc)
- }()
- return kvc
- }
- // Generate a list with test orders.
- func generateTestOrdersList(count int) OrderList {
- l := make(OrderList, count)
- idx := 0
- for kv := range generateTestOrders(count) {
- l[idx] = kv.Value.(*Order)
- idx++
- }
- return l
- }
- // Create a job that leads to a an event every 2 seconds.
- func createJob(t *testing.T, descr string, delete bool) *Job {
- cf := func(now *time.Time) (bool, bool) { return now.Seconds()%2 == 0, delete }
- tf := func(id string) { t.Logf("Performed job %s\n", id) }
- return NewJob("test-server-"+descr, cf, tf)
- }
- // Create a SML structure.
- func createSmlStructure() *TagNode {
- root := NewTagNode("root")
- root.AppendText("Text A")
- root.AppendText("Text B")
- root.AppendTaggedText("comment", "A first comment.")
- subA := root.AppendTag("sub-a:1st:important")
- subA.AppendText("Text A.A")
- root.AppendTaggedText("comment", "A second comment.")
- subB := root.AppendTag("sub-b:2nd")
- subB.AppendText("Text B.A")
- subB.AppendTaggedText("raw", "Any raw text with {, }, and ^.")
- return root
- }
- // Do some work.
- func work(n int) int {
- if n < 0 {
- return 0
- }
- return n * work(n-1)
- }
- //--------------------
- // TEST LOGIN EVENT HANDLER
- //--------------------
- // Prepare payload.
- type PreparePayload struct {
- userId string
- password string
- }
- // Login payload.
- type LoginPayload struct {
- password string
- }
- // Reset payload.
- type ResetPayload struct{}
- // Unlock payload.
- type UnlockPayload struct{}
- // Login handler tyoe.
- type LoginHandler struct {
- userId string
- password string
- illegalLoginCounter int
- locked bool
- }
- // Create a new login handler.
- func NewLoginHandler() *LoginHandler {
- return new(LoginHandler)
- }
- // Return the initial state.
- func (lh *LoginHandler) Init() string {
- return "New"
- }
- // Terminate the handler.
- func (lh *LoginHandler) Terminate(string, interface{}) string {
- return "LoggedIn"
- }
- // Handler for state: "New".
- func (lh *LoginHandler) HandleStateNew(c *Condition) (string, interface{}) {
- switch pld := c.Payload.(type) {
- case *PreparePayload:
- lh.userId = pld.userId
- lh.password = pld.password
- lh.illegalLoginCounter = 0
- lh.locked = false
- log.Printf("User '%v' prepared.", lh.userId)
- return "Authenticating", nil
- case *LoginPayload:
- log.Printf("Illegal login, handler not initialized!")
- return "New", false
- case Timeout:
- log.Printf("Timeout, terminate handler!")
- return "Terminate", nil
- }
- log.Printf("Illegal payload '%v' during state 'new'!", c.Payload)
- return "New", nil
- }
- // Handler for state: "Authenticating".
- func (lh *LoginHandler) HandleStateAuthenticating(c *Condition) (string, interface{}) {
- switch pld := c.Payload.(type) {
- case *LoginPayload:
- if pld.password == lh.password {
- lh.illegalLoginCounter = 0
- lh.locked = false
- log.Printf("User '%v' logged in.", lh.userId)
- return "Terminate", true
- }
- log.Printf("User '%v' used illegal password.", lh.userId)
- lh.illegalLoginCounter++
- if lh.illegalLoginCounter == 3 {
- lh.locked = true
- log.Printf("User '%v' locked!", lh.userId)
- return "Locked", false
- }
- return "Authenticating", false
- case *UnlockPayload:
- log.Printf("No need to unlock user '%v'!", lh.userId)
- return "Authenticating", nil
- case *ResetPayload, Timeout:
- lh.illegalLoginCounter = 0
- lh.locked = false
- log.Printf("User '%v' resetted.", lh.userId)
- return "Authenticating", nil
- }
- log.Printf("Illegal payload '%v' during state 'authenticating'!", c.Payload)
- return "Authenticating", nil
- }
- // Handler for state: "Locked".
- func (lh *LoginHandler) HandleStateLocked(c *Condition) (string, interface{}) {
- switch pld := c.Payload.(type) {
- case *LoginPayload:
- log.Printf("User '%v' login rejected, user is locked!", lh.userId)
- return "Locked", false
- case *ResetPayload, *UnlockPayload, Timeout:
- lh.illegalLoginCounter = 0
- lh.locked = false
- log.Printf("User '%v' resetted / unlocked.", lh.userId)
- return "Authenticating", nil
- }
- log.Printf("Illegal payload '%v' during state 'loacked'!", c.Payload)
- return "Locked", nil
- }
- /*
- EOF
- */
|