github.com/haraldrudell/parl@v0.4.176/g0/go_test.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package g0 7 8 import ( 9 "context" 10 "errors" 11 "strings" 12 "sync" 13 "testing" 14 15 "github.com/haraldrudell/parl" 16 "github.com/haraldrudell/parl/goid" 17 "github.com/haraldrudell/parl/pruntime" 18 ) 19 20 func TestGo(t *testing.T) { 21 var err = errors.New("x") 22 var label = "label" 23 24 var goGroup parl.GoGroup 25 var g0 parl.Go 26 var g0Impl *Go 27 var subGroup parl.SubGroup 28 var subGo parl.SubGo 29 var goError parl.GoError 30 var ok bool 31 var ctx0, ctx context.Context 32 33 // Register() AddError() Go() SubGo() SubGroup() Done() Wait() 34 // WaitCh() Cancel() Context() ThreadInfo() Creator() 35 // GoRoutine() GoID() EntityID() 36 var g parl.Go 37 var goGroup2 parl.GoGroup 38 var reset = func() { 39 goGroup2 = NewGoGroup(context.Background()) 40 g = goGroup.Go() 41 } 42 _ = g 43 _ = reset 44 _ = goGroup2 45 46 // Register() AddError() Go() SubGo() SubGroup() Done() ThreadInfo() 47 // GoID() Wait() 48 goGroup = NewGoGroup(context.Background()) 49 g0 = goGroup.Go() 50 g0Impl = g0.(*Go) 51 if g0Impl.endCh.IsClosed() { 52 t.Error("Go terminated") 53 } 54 g0.Register(label) 55 if !g0.GoID().IsValid() { 56 t.Error("Go.GoID bad") 57 } 58 if g0.ThreadInfo().Name() != label { 59 t.Error("g0.Register bad") 60 } 61 if subGroup = g0.SubGroup(); subGroup == nil { 62 t.Error("Go.SubGroup bad") 63 } 64 if subGo = g0.SubGo(); subGo == nil { 65 t.Error("Go.SubGo bad") 66 } 67 g0.AddError(err) 68 if goError = <-goGroup.Ch(); !errors.Is(goError.Err(), err) || 69 goError.ErrContext() != parl.GeNonFatal { 70 t.Error("g0.AddError bad") 71 } 72 g0.Done(&err) 73 if goError = <-goGroup.Ch(); !errors.Is(goError.Err(), err) || 74 goError.ErrContext() != parl.GeExit { 75 t.Error("g0.Done bad") 76 } 77 if _, ok = <-goGroup.Ch(); ok { 78 t.Error("g0.Done goGroup errch did not close") 79 } 80 g0.Wait() 81 82 // Cancel() Context() 83 goGroup = NewGoGroup(context.Background()) 84 ctx0 = goGroup.Context() 85 g0 = goGroup.Go() 86 g0.Cancel() 87 if ctx = g0.Context(); ctx.Err() == nil { 88 t.Error("Go.Cancel did not cancel context") 89 } 90 if ctx0 != ctx { 91 t.Error("Go.Context bad") 92 } 93 94 // String(): "go:34:testing.(*T).Run()-testing.go:1629" 95 t.Log(g0.String()) 96 //t.Fail() 97 } 98 99 type T struct { 100 g0 parl.Go 101 label string 102 cL *pruntime.CodeLocation 103 goroutineThreadID *parl.ThreadID 104 wg sync.WaitGroup 105 } 106 107 // tt.g must have Register and NewCodeLocation on same line 108 // - tt.g is the goFunction of a goroutine 109 func (tt *T) g() { tt.g0.Register(tt.label); tt.h(pruntime.NewCodeLocation(0)) } 110 func (tt *T) h(g0RegisterLine *pruntime.CodeLocation) { 111 defer tt.wg.Done() 112 113 *tt.cL = *g0RegisterLine 114 *tt.goroutineThreadID = goid.GoID() 115 } 116 117 func TestGo_Frames(t *testing.T) { 118 var expLabel = "LABEL" 119 var expThreadID = goid.GoID() 120 var cL *pruntime.CodeLocation 121 var g0 parl.Go 122 var subGo parl.SubGo 123 var subGroup parl.SubGroup 124 var goFunctionCL pruntime.CodeLocation 125 var goroutineThreadID parl.ThreadID 126 127 var goGroup parl.GoGroup = NewGoGroup(context.Background()) 128 var parentGo parl.Go = goGroup.Go() 129 130 // Go.Go(): Go.String() has caller location 131 // - the location is stored in the thread field 132 // g0 and cL assignments on same line 133 g0, cL = parentGo.Go(), pruntime.NewCodeLocation(0) 134 if !strings.HasSuffix(g0.String(), cL.Short()) { 135 var _ = (&Go{}).String 136 var _ ThreadData 137 t.Errorf("Go.Go: Go.String BAD: %q exp suffix: %q", g0.String(), cL.Short()) 138 } 139 140 // Go.Go(): before g0.Register(), there is an intermediate createLocation 141 // - this has only creator set 142 id, cre := g0.Creator() 143 var _ ThreadData 144 // Go.Go() before g0.Register: Go.ThreadInfo: cre:g0.TestGo_Frames()-go_test.go:113 creatorID: 4 145 t.Logf("Go.Go() before g0.Register: Go.ThreadInfo: %s creatorID: %s", g0.ThreadInfo(), id) 146 if id != expThreadID { 147 t.Errorf("Go.CreatorID: %q exp %q", id, expThreadID) 148 } 149 if cre.Short() != cL.Short() { 150 t.Errorf("Go.Creator Short: %q exp %q", cre.Short(), cL.Short()) 151 } 152 153 // Go.Register(): updates ThreadID goFuncLocation and label 154 tt := T{g0: g0, label: expLabel, cL: &goFunctionCL, goroutineThreadID: &goroutineThreadID} 155 tt.wg.Add(1) 156 go tt.g() 157 tt.wg.Wait() 158 var threadData = g0.ThreadInfo() 159 if threadData.ThreadID() != goroutineThreadID { 160 t.Errorf("Go ThreadID: %q exp %q", threadData.ThreadID(), goroutineThreadID) 161 } 162 t.Logf("goFunction: %s", threadData.Func().Dump()) 163 t.Logf("goFunctionCL: %s", goFunctionCL.Dump()) 164 if threadData.Func().Short() != goFunctionCL.Short() { 165 t.Errorf("Go goFunction Short: %q exp %q", threadData.Func().Short(), goFunctionCL.Short()) 166 } 167 if threadData.Name() != expLabel { 168 t.Errorf("Go thread label: %q exp %q", threadData.Name(), expLabel) 169 } 170 171 // g0.SubGo() g0.SubGroup() have invoking creator location 172 var _ parl.Go 173 subGo, cL = g0.SubGo(), pruntime.NewCodeLocation(0) 174 if !strings.HasSuffix(subGo.String(), cL.Short()) { 175 t.Errorf("g0.SubGo() invoker location: %q expPrefix %q", subGo.String(), cL.Short()) 176 } 177 subGroup, cL = g0.SubGroup(), pruntime.NewCodeLocation(0) 178 if !strings.HasSuffix(subGroup.String(), cL.Short()) { 179 t.Errorf("g0.SubGroup() invoker location: %q expPrefix %q", subGroup.String(), cL.Short()) 180 } 181 }