Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 123 additions & 58 deletions pkg/raised/design_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package raised

import (
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"strings"
Expand Down Expand Up @@ -89,7 +87,7 @@ func BenchmarkDesign_propRces64r4(b *testing.B) {
}

func BenchmarkDesign_propRaiseds64r4(b *testing.B) {
ef := makeRaisedPropChain(64, 4)
ef := makeRaisedPropChain(64, 4, errPropSentinel)
for b.Loop() {
_ = ef()
}
Expand Down Expand Up @@ -117,7 +115,7 @@ func BenchmarkDesign_propRceTraces64r4(b *testing.B) {
}

func BenchmarkDesign_propRaisedTraces64r4(b *testing.B) {
ef := makeRaisedPropChain(64, 4)
ef := makeRaisedPropChain(64, 4, errPropSentinel)
for b.Loop() {
_ = fmt.Sprintf("%+v", ef()) // using fmt results in 1 extra alloc...
}
Expand Down Expand Up @@ -145,7 +143,7 @@ func BenchmarkDesign_propRces64r8(b *testing.B) {
}

func BenchmarkDesign_propRaiseds64r8(b *testing.B) {
ef := makeRaisedPropChain(64, 8)
ef := makeRaisedPropChain(64, 8, errPropSentinel)
for b.Loop() {
_ = ef()
}
Expand Down Expand Up @@ -173,7 +171,7 @@ func BenchmarkDesign_propRceTraces64r8(b *testing.B) {
}

func BenchmarkDesign_propRaisedTraces64r8(b *testing.B) {
ef := makeRaisedPropChain(64, 8)
ef := makeRaisedPropChain(64, 8, errPropSentinel)
for b.Loop() {
_ = fmt.Sprintf("%+v", ef()) // using fmt results in 1 extra alloc...
}
Expand Down Expand Up @@ -201,7 +199,7 @@ func BenchmarkDesign_propRces256r16(b *testing.B) {
}

func BenchmarkDesign_propRaiseds256r16(b *testing.B) {
ef := makeRaisedPropChain(256, 16)
ef := makeRaisedPropChain(256, 16, errPropSentinel)
for b.Loop() {
_ = ef()
}
Expand Down Expand Up @@ -229,7 +227,7 @@ func BenchmarkDesign_propRceTraces256r16(b *testing.B) {
}

func BenchmarkDesign_propRaisedTraces256r16(b *testing.B) {
ef := makeRaisedPropChain(256, 16)
ef := makeRaisedPropChain(256, 16, errPropSentinel)
for b.Loop() {
_ = fmt.Sprintf("%+v", ef()) // using fmt results in 1 extra alloc...
}
Expand All @@ -242,9 +240,123 @@ func BenchmarkDesign_propRaisedPCCacheTraces256r16(b *testing.B) {
}
}

// ====================================================================================
// Keying errors returned by raised.Trace
//
// To run those benchmarks use:
// go test ./pkg/raised -bench Design_key -benchmem
//

func BenchmarkDesign_keyRaised_Stables64r4(b *testing.B) {

ek, fail := NewErrorKeyer[pkg](nil)
if nil != fail {
b.Fatalf("failed instantiating ErrorKeyer, got error %v", fail)
}

ef := makeRaisedPropChain(64, 4, errPropSentinel)
err := ef()
for b.Loop() {
ek.Key(err)
}
}

func BenchmarkDesign_keyRaised_Stables64r8(b *testing.B) {

ek, fail := NewErrorKeyer[pkg](nil)
if nil != fail {
b.Fatalf("failed instantiating ErrorKeyer, got error %v", fail)
}

ef := makeRaisedPropChain(64, 8, errPropSentinel)
err := ef()
for b.Loop() {
ek.Key(err)
}
}

func BenchmarkDesign_keyRaised_Stables64r16(b *testing.B) {

ek, fail := NewErrorKeyer[pkg](nil)
if nil != fail {
b.Fatalf("failed instantiating ErrorKeyer, got error %v", fail)
}

ef := makeRaisedPropChain(64, 16, errPropSentinel)
err := ef()
for b.Loop() {
ek.Key(err)
}
}

func BenchmarkDesign_keyRaised_Unstables64r4(b *testing.B) {

ek, fail := NewErrorKeyer[pkg](nil)
if nil != fail {
b.Fatalf("failed instantiating ErrorKeyer, got error %v", fail)
}

erts := make([]error, 4096)
for i := range 4096 {
cause := fmt.Errorf("root cause %d", i)
ef := makeRaisedPropChain(64, 4, cause)
erts[i] = ef()
}

c := 0
for b.Loop() {
ek.Key(erts[c%4096])
c++
}
}

func BenchmarkDesign_keyRaised_Unstables64r8(b *testing.B) {

ek, fail := NewErrorKeyer[pkg](nil)
if nil != fail {
b.Fatalf("failed instantiating ErrorKeyer, got error %v", fail)
}

erts := make([]error, 4096)
for i := range 4096 {
cause := fmt.Errorf("root cause %d", i)
ef := makeRaisedPropChain(64, 8, cause)
erts[i] = ef()
}

c := 0
for b.Loop() {
ek.Key(erts[c%4096])
c++
}
}

func BenchmarkDesign_keyRaised_Unstables64r16(b *testing.B) {

ek, fail := NewErrorKeyer[pkg](nil)
if nil != fail {
b.Fatalf("failed instantiating ErrorKeyer, got error %v", fail)
}

erts := make([]error, 4096)
for i := range 4096 {
cause := fmt.Errorf("root cause %d", i)
ef := makeRaisedPropChain(64, 16, cause)
erts[i] = ef()
}

c := 0
for b.Loop() {
ek.Key(erts[c%4096])
c++
}
}

// ====================================================================================
var errPropSentinel = NewSentinel("ERROR(245): propagation sentinel")

type errfunc = func() error
// makeRaisedPropChain is defined in only_test.go
// as it is also used to support other tests

func makeErrorfPropChain(strsz int, chnsz int) errfunc {
makeNextFunc := func(fls int, prev errfunc) errfunc {
Expand Down Expand Up @@ -321,43 +433,6 @@ func makeRcePropChain(strsz int, chnsz int) errfunc {
return erf
}

func makeRaisedPropChain(strsz int, chnsz int) errfunc {
makeNextFunc := func(fls int, prev errfunc) errfunc {
msg := fmt.Sprintf("[%d]: %s", fls, rndString(strsz))
switch fls % 4 {
case 0:
return func() error {
return Trace(prev(), msg)
}
case 1:
return func() error {
return Trace(prev(), msg)
}
case 2:
return func() error {
return Trace(prev(), msg)
}
case 3:
return func() error {
return Trace(prev(), msg)
}
default:
return func() error {
return Trace(prev(), msg)
}
}
}

erf := func() error {
return errPropSentinel
}
for i := range chnsz {
erf = makeNextFunc(i, erf)
}

return erf
}

func makeRaisedPCCachePropChain(strsz int, chnsz int) errfunc {
makeNextFunc := func(fls int, prev errfunc) errfunc {
msg := fmt.Sprintf("[%d]: %s", fls, rndString(strsz))
Expand Down Expand Up @@ -396,7 +471,7 @@ func makeRaisedPCCachePropChain(strsz int, chnsz int) errfunc {
}

func TestDesign_propRaised(t *testing.T) {
errFunc := makeRaisedPropChain(32, 12)
errFunc := makeRaisedPropChain(32, 12, errPropSentinel)
err := errFunc()
t.Logf("err -> %v", err)
t.Logf("err %%s \n%s", err)
Expand All @@ -405,7 +480,7 @@ func TestDesign_propRaised(t *testing.T) {
}

func TestDesign_propRaised02(t *testing.T) {
errFunc := makeRaisedPropChain(32, 16)
errFunc := makeRaisedPropChain(32, 16, errPropSentinel)
err := errFunc()
t.Logf("err1 -> \n%v", err)
err = errFunc()
Expand Down Expand Up @@ -930,16 +1005,6 @@ type errStr struct {
// ====================================================================================
// Utilities

var rawB64 = base64.StdEncoding.WithPadding(base64.NoPadding)

// rndString returns a base64 string encoding sz random bytes.
func rndString(sz int) string {
buf := make([]byte, sz)
rand.Read(buf)

return rawB64.EncodeToString(buf)
}

func TestDesign_RndString(t *testing.T) {
t.Logf("rndString(32) -> %s", rndString(32))
t.Logf("rndString(64) -> %s", rndString(64))
Expand Down
4 changes: 2 additions & 2 deletions pkg/raised/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func TestError_Classify(t *testing.T) {

func TestError_Compression(t *testing.T) {
// produce a chain longer than traceSize to trigger compression
errFunc := makeRaisedPropChain(16, traceSize+4)
errFunc := makeRaisedPropChain(16, traceSize+4, errPropSentinel)
err := errFunc()

et, ok := err.(*errTrace)
Expand All @@ -118,7 +118,7 @@ func TestError_Compression(t *testing.T) {
}

func TestError_Show(t *testing.T) {
errFunc := makeRaisedPropChain(16, traceSize+8)
errFunc := makeRaisedPropChain(16, traceSize+8, errPropSentinel)
err := errFunc()

t.Logf("err.Error() ->\n%v", err)
Expand Down
51 changes: 51 additions & 0 deletions pkg/raised/keying_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,57 @@ func TestKeying_Key_NoResolvableTerminal(t *testing.T) {
t.Skip("nil-terminal path requires internal construction; covered by TestKey_NonTracedError and TestKey_NilError")
}

// ---- Key: correctness tests using RaisedPropChain ----

func TestKeying_KeyPropChain_SamePropChainSameCauseSentinel_Equal(t *testing.T) {
ek := mustKeyer(t, nil)
s1 := NewSentinelError[familyA]("ERROR(110): sentinel can be recognized")

// ef1 propagates sentinel
ef1 := makeRaisedPropChain(64, 25, s1)
k1, ok1 := ek.Key(ef1())

// ef2 propagates an error that wraps s1
err2 := fmt.Errorf("I should be ignored %w", s1)
ef2 := makeRaisedPropChain(64, 25, err2)
k2, ok2 := ek.Key(ef2())

if !ok1 || !ok2 {
t.Fatalf("Key returned false: ok1=%v ok2=%v", ok1, ok2)
}

// RMQ: note that the error propagated by ef2 is different than the one propagated by ef1
// but they have same sentinel, hence keys are the same.
if k1 != k2 {
t.Errorf("expected same keys, got %X != %X", k1, k2)
}
}

func TestKeying_KeyPropChain_SamePropChainDistinctCauseSentinel_NotEqual(t *testing.T) {
ek := mustKeyer(t, nil)
s1 := NewSentinelError[familyA]("ERROR(110): distinct phantom types, distinct sentinels")
s2 := NewSentinelError[familyB]("ERROR(110): distinct phantom types, distinct sentinels")

// ef1 propagates sentinel s1
ef1 := makeRaisedPropChain(64, 25, s1)
k1, ok1 := ek.Key(ef1())

// ef2 propagates sentinel s2
ef2 := makeRaisedPropChain(64, 25, s2)
k2, ok2 := ek.Key(ef2())

if !ok1 || !ok2 {
t.Fatalf("Key returned false: ok1=%v ok2=%v", ok1, ok2)
}

if k1 == k2 {
t.Error("expected distinct keys")
}

t.Logf("k1 -> %X", k1[:])
t.Logf("k2 -> %X", k2[:])
}

// ---- Key: correctness tests ----

func TestKeying_Key_SameCallSiteSameCause_Equal(t *testing.T) {
Expand Down
Loading
Loading