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  }