This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncodeBase62Zero(t *testing.T) {
|
||||
tst.AssertEqual(t, EncodeBase62(0), "0")
|
||||
}
|
||||
|
||||
func TestEncodeBase62Small(t *testing.T) {
|
||||
tst.AssertEqual(t, EncodeBase62(1), "1")
|
||||
tst.AssertEqual(t, EncodeBase62(9), "9")
|
||||
tst.AssertEqual(t, EncodeBase62(10), "A")
|
||||
tst.AssertEqual(t, EncodeBase62(35), "Z")
|
||||
tst.AssertEqual(t, EncodeBase62(36), "a")
|
||||
tst.AssertEqual(t, EncodeBase62(61), "z")
|
||||
tst.AssertEqual(t, EncodeBase62(62), "10")
|
||||
}
|
||||
|
||||
func TestDecodeBase62Empty(t *testing.T) {
|
||||
_, err := DecodeBase62("")
|
||||
if err == nil {
|
||||
t.Errorf("expected error on empty input")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeBase62Invalid(t *testing.T) {
|
||||
_, err := DecodeBase62("foo!bar")
|
||||
if err == nil {
|
||||
t.Errorf("expected error on invalid character")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeBase62Basic(t *testing.T) {
|
||||
v, err := DecodeBase62("0")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, v, uint64(0))
|
||||
|
||||
v, err = DecodeBase62("10")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, v, uint64(62))
|
||||
}
|
||||
|
||||
func TestEncodeDecodeBase62RoundTrip(t *testing.T) {
|
||||
for _, n := range []uint64{0, 1, 61, 62, 100, 12345, 1<<32 - 1, 1 << 40} {
|
||||
s := EncodeBase62(n)
|
||||
v, err := DecodeBase62(s)
|
||||
if err != nil {
|
||||
t.Errorf("decode error for %d (encoded %q): %v", n, s, err)
|
||||
continue
|
||||
}
|
||||
tst.AssertEqual(t, v, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandBase62Length(t *testing.T) {
|
||||
for _, l := range []int{0, 1, 8, 32, 64} {
|
||||
s := RandBase62(l)
|
||||
tst.AssertEqual(t, len(s), l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandBase62Alphabet(t *testing.T) {
|
||||
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
s := RandBase62(256)
|
||||
for _, r := range s {
|
||||
if !strings.ContainsRune(alphabet, r) {
|
||||
t.Errorf("character %q not in base62 alphabet", string(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandBase62Distinct(t *testing.T) {
|
||||
a := RandBase62(32)
|
||||
b := RandBase62(32)
|
||||
if a == b {
|
||||
t.Errorf("two base62 random strings of length 32 should not be equal")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatBoolTrue(t *testing.T) {
|
||||
tst.AssertEqual(t, FormatBool(true, "yes", "no"), "yes")
|
||||
}
|
||||
|
||||
func TestFormatBoolFalse(t *testing.T) {
|
||||
tst.AssertEqual(t, FormatBool(false, "yes", "no"), "no")
|
||||
}
|
||||
|
||||
func TestConditional(t *testing.T) {
|
||||
tst.AssertEqual(t, Conditional(true, 1, 2), 1)
|
||||
tst.AssertEqual(t, Conditional(false, 1, 2), 2)
|
||||
tst.AssertEqual(t, Conditional(true, "a", "b"), "a")
|
||||
tst.AssertEqual(t, Conditional(false, "a", "b"), "b")
|
||||
}
|
||||
|
||||
func TestConditionalFn00(t *testing.T) {
|
||||
tst.AssertEqual(t, ConditionalFn00(true, 10, 20), 10)
|
||||
tst.AssertEqual(t, ConditionalFn00(false, 10, 20), 20)
|
||||
}
|
||||
|
||||
func TestConditionalFn10Lazy(t *testing.T) {
|
||||
called := false
|
||||
v := ConditionalFn10(false, func() int {
|
||||
called = true
|
||||
return 1
|
||||
}, 99)
|
||||
tst.AssertEqual(t, v, 99)
|
||||
tst.AssertEqual(t, called, false)
|
||||
|
||||
v = ConditionalFn10(true, func() int {
|
||||
called = true
|
||||
return 1
|
||||
}, 99)
|
||||
tst.AssertEqual(t, v, 1)
|
||||
tst.AssertEqual(t, called, true)
|
||||
}
|
||||
|
||||
func TestConditionalFn01Lazy(t *testing.T) {
|
||||
called := false
|
||||
v := ConditionalFn01(true, 1, func() int {
|
||||
called = true
|
||||
return 99
|
||||
})
|
||||
tst.AssertEqual(t, v, 1)
|
||||
tst.AssertEqual(t, called, false)
|
||||
|
||||
v = ConditionalFn01(false, 1, func() int {
|
||||
called = true
|
||||
return 99
|
||||
})
|
||||
tst.AssertEqual(t, v, 99)
|
||||
tst.AssertEqual(t, called, true)
|
||||
}
|
||||
|
||||
func TestConditionalFn11Lazy(t *testing.T) {
|
||||
calledT := false
|
||||
calledF := false
|
||||
|
||||
v := ConditionalFn11(true,
|
||||
func() int { calledT = true; return 1 },
|
||||
func() int { calledF = true; return 2 },
|
||||
)
|
||||
tst.AssertEqual(t, v, 1)
|
||||
tst.AssertEqual(t, calledT, true)
|
||||
tst.AssertEqual(t, calledF, false)
|
||||
|
||||
calledT = false
|
||||
calledF = false
|
||||
|
||||
v = ConditionalFn11(false,
|
||||
func() int { calledT = true; return 1 },
|
||||
func() int { calledF = true; return 2 },
|
||||
)
|
||||
tst.AssertEqual(t, v, 2)
|
||||
tst.AssertEqual(t, calledT, false)
|
||||
tst.AssertEqual(t, calledF, true)
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatBytesToSI(t *testing.T) {
|
||||
tst.AssertEqual(t, FormatBytesToSI(0), "0 B")
|
||||
tst.AssertEqual(t, FormatBytesToSI(999), "999 B")
|
||||
tst.AssertEqual(t, FormatBytesToSI(1000), "1.0 kB")
|
||||
tst.AssertEqual(t, FormatBytesToSI(1500), "1.5 kB")
|
||||
tst.AssertEqual(t, FormatBytesToSI(1000*1000), "1.0 MB")
|
||||
tst.AssertEqual(t, FormatBytesToSI(1000*1000*1000), "1.0 GB")
|
||||
}
|
||||
|
||||
func TestFormatBytes(t *testing.T) {
|
||||
tst.AssertEqual(t, FormatBytes(0), "0 B")
|
||||
tst.AssertEqual(t, FormatBytes(1023), "1023 B")
|
||||
tst.AssertEqual(t, FormatBytes(1024), "1.0 KiB")
|
||||
tst.AssertEqual(t, FormatBytes(1024*1024), "1.0 MiB")
|
||||
tst.AssertEqual(t, FormatBytes(1024*1024*1024), "1.0 GiB")
|
||||
tst.AssertEqual(t, FormatBytes(1536), "1.5 KiB")
|
||||
}
|
||||
|
||||
func TestBytesXOR(t *testing.T) {
|
||||
a := []byte{0x01, 0x02, 0x03, 0xFF}
|
||||
b := []byte{0xFF, 0xFE, 0xFD, 0x00}
|
||||
|
||||
r, err := BytesXOR(a, b)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
expected := []byte{0xFE, 0xFC, 0xFE, 0xFF}
|
||||
tst.AssertArrayEqual(t, r, expected)
|
||||
}
|
||||
|
||||
func TestBytesXORLengthMismatch(t *testing.T) {
|
||||
a := []byte{0x01, 0x02}
|
||||
b := []byte{0x01, 0x02, 0x03}
|
||||
|
||||
_, err := BytesXOR(a, b)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error on length mismatch, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytesXOREmpty(t *testing.T) {
|
||||
r, err := BytesXOR([]byte{}, []byte{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, len(r), 0)
|
||||
}
|
||||
|
||||
func TestBytesXORSelfIsZero(t *testing.T) {
|
||||
a := []byte{0xAB, 0xCD, 0xEF}
|
||||
r, err := BytesXOR(a, a)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
for i, v := range r {
|
||||
if v != 0 {
|
||||
t.Errorf("expected zero at index %d, got %#x", i, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type stringerImpl struct{ v string }
|
||||
|
||||
func (s stringerImpl) String() string { return s.v }
|
||||
|
||||
func TestCoalesceWithValue(t *testing.T) {
|
||||
v := 42
|
||||
tst.AssertEqual(t, Coalesce(&v, 0), 42)
|
||||
}
|
||||
|
||||
func TestCoalesceWithNil(t *testing.T) {
|
||||
var p *int
|
||||
tst.AssertEqual(t, Coalesce(p, 99), 99)
|
||||
}
|
||||
|
||||
func TestCoalesceOpt(t *testing.T) {
|
||||
v := 1
|
||||
w := 2
|
||||
tst.AssertDeRefEqual(t, CoalesceOpt(&v, &w), 1)
|
||||
|
||||
var p *int
|
||||
tst.AssertDeRefEqual(t, CoalesceOpt(p, &w), 2)
|
||||
|
||||
tst.AssertPtrEqual(t, CoalesceOpt[int](nil, nil), nil)
|
||||
}
|
||||
|
||||
func TestCoalesce3(t *testing.T) {
|
||||
v := 1
|
||||
w := 2
|
||||
tst.AssertEqual(t, Coalesce3(&v, &w, 99), 1)
|
||||
tst.AssertEqual(t, Coalesce3[int](nil, &w, 99), 2)
|
||||
tst.AssertEqual(t, Coalesce3[int](nil, nil, 99), 99)
|
||||
}
|
||||
|
||||
func TestCoalesce3Opt(t *testing.T) {
|
||||
v := 1
|
||||
tst.AssertDeRefEqual(t, Coalesce3Opt[int](nil, nil, &v), 1)
|
||||
tst.AssertPtrEqual(t, Coalesce3Opt[int](nil, nil, nil), nil)
|
||||
}
|
||||
|
||||
func TestCoalesce4(t *testing.T) {
|
||||
v := 1
|
||||
w := 2
|
||||
x := 3
|
||||
tst.AssertEqual(t, Coalesce4(&v, &w, &x, 99), 1)
|
||||
tst.AssertEqual(t, Coalesce4[int](nil, &w, &x, 99), 2)
|
||||
tst.AssertEqual(t, Coalesce4[int](nil, nil, &x, 99), 3)
|
||||
tst.AssertEqual(t, Coalesce4[int](nil, nil, nil, 99), 99)
|
||||
}
|
||||
|
||||
func TestCoalesce4Opt(t *testing.T) {
|
||||
v := 4
|
||||
tst.AssertDeRefEqual(t, Coalesce4Opt[int](nil, nil, nil, &v), 4)
|
||||
tst.AssertPtrEqual(t, Coalesce4Opt[int](nil, nil, nil, nil), nil)
|
||||
}
|
||||
|
||||
func TestCoalesceString(t *testing.T) {
|
||||
s := "hello"
|
||||
tst.AssertEqual(t, CoalesceString(&s, "def"), "hello")
|
||||
tst.AssertEqual(t, CoalesceString(nil, "def"), "def")
|
||||
}
|
||||
|
||||
func TestCoalesceInt(t *testing.T) {
|
||||
v := 5
|
||||
tst.AssertEqual(t, CoalesceInt(&v, 99), 5)
|
||||
tst.AssertEqual(t, CoalesceInt(nil, 99), 99)
|
||||
}
|
||||
|
||||
func TestCoalesceInt32(t *testing.T) {
|
||||
v := int32(7)
|
||||
tst.AssertEqual(t, CoalesceInt32(&v, 99), int32(7))
|
||||
tst.AssertEqual(t, CoalesceInt32(nil, 99), int32(99))
|
||||
}
|
||||
|
||||
func TestCoalesceBool(t *testing.T) {
|
||||
v := true
|
||||
tst.AssertEqual(t, CoalesceBool(&v, false), true)
|
||||
tst.AssertEqual(t, CoalesceBool(nil, true), true)
|
||||
tst.AssertEqual(t, CoalesceBool(nil, false), false)
|
||||
}
|
||||
|
||||
func TestCoalesceTime(t *testing.T) {
|
||||
now := time.Now()
|
||||
def := time.Unix(0, 0)
|
||||
tst.AssertEqual(t, CoalesceTime(&now, def), now)
|
||||
tst.AssertEqual(t, CoalesceTime(nil, def), def)
|
||||
}
|
||||
|
||||
func TestCoalesceStringer(t *testing.T) {
|
||||
s := stringerImpl{v: "hi"}
|
||||
tst.AssertEqual(t, CoalesceStringer(s, "def"), "hi")
|
||||
|
||||
var nilStringer *stringerImpl
|
||||
tst.AssertEqual(t, CoalesceStringer(nilStringer, "def"), "def")
|
||||
}
|
||||
|
||||
func TestCoalesceDefault(t *testing.T) {
|
||||
tst.AssertEqual(t, CoalesceDefault(0, 99), 99)
|
||||
tst.AssertEqual(t, CoalesceDefault(5, 99), 5)
|
||||
tst.AssertEqual(t, CoalesceDefault("", "def"), "def")
|
||||
tst.AssertEqual(t, CoalesceDefault("v", "def"), "v")
|
||||
}
|
||||
|
||||
func TestSafeCastMatching(t *testing.T) {
|
||||
var v any = "hello"
|
||||
tst.AssertEqual(t, SafeCast(v, "default"), "hello")
|
||||
}
|
||||
|
||||
func TestSafeCastMismatch(t *testing.T) {
|
||||
var v any = 42
|
||||
tst.AssertEqual(t, SafeCast(v, "default"), "default")
|
||||
}
|
||||
|
||||
func TestSafeCastNil(t *testing.T) {
|
||||
tst.AssertEqual(t, SafeCast(nil, 99), 99)
|
||||
}
|
||||
|
||||
func TestCoalesceDblPtrWithValue(t *testing.T) {
|
||||
v := 1
|
||||
pv := &v
|
||||
tst.AssertDeRefEqual(t, CoalesceDblPtr(&pv, nil), 1)
|
||||
}
|
||||
|
||||
func TestCoalesceDblPtrFallback(t *testing.T) {
|
||||
w := 2
|
||||
tst.AssertDeRefEqual(t, CoalesceDblPtr[int](nil, &w), 2)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompareIntArrLess(t *testing.T) {
|
||||
tst.AssertEqual(t, CompareIntArr([]int{1, 2, 3}, []int{1, 2, 4}), true)
|
||||
tst.AssertEqual(t, CompareIntArr([]int{0}, []int{1}), true)
|
||||
}
|
||||
|
||||
func TestCompareIntArrGreater(t *testing.T) {
|
||||
tst.AssertEqual(t, CompareIntArr([]int{1, 2, 5}, []int{1, 2, 4}), false)
|
||||
tst.AssertEqual(t, CompareIntArr([]int{2}, []int{1}), false)
|
||||
}
|
||||
|
||||
func TestCompareIntArrEqual(t *testing.T) {
|
||||
tst.AssertEqual(t, CompareIntArr([]int{1, 2, 3}, []int{1, 2, 3}), false)
|
||||
tst.AssertEqual(t, CompareIntArr([]int{}, []int{}), false)
|
||||
}
|
||||
|
||||
func TestCompareArrLess(t *testing.T) {
|
||||
tst.AssertEqual(t, CompareArr([]int{1, 2, 3}, []int{1, 2, 4}), -1)
|
||||
}
|
||||
|
||||
func TestCompareArrGreater(t *testing.T) {
|
||||
r := CompareArr([]int{1, 2, 5}, []int{1, 2, 4})
|
||||
if r <= 0 {
|
||||
t.Errorf("expected positive, got %d", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareArrEqual(t *testing.T) {
|
||||
tst.AssertEqual(t, CompareArr([]int{1, 2, 3}, []int{1, 2, 3}), 0)
|
||||
tst.AssertEqual(t, CompareArr([]int{}, []int{}), 0)
|
||||
}
|
||||
|
||||
func TestCompareString(t *testing.T) {
|
||||
tst.AssertEqual(t, CompareString("a", "b"), -1)
|
||||
tst.AssertEqual(t, CompareString("b", "a"), 1)
|
||||
tst.AssertEqual(t, CompareString("a", "a"), 0)
|
||||
}
|
||||
|
||||
func TestCompareInt(t *testing.T) {
|
||||
tst.AssertEqual(t, CompareInt(1, 2), -1)
|
||||
tst.AssertEqual(t, CompareInt(2, 1), 1)
|
||||
tst.AssertEqual(t, CompareInt(2, 2), 0)
|
||||
}
|
||||
|
||||
func TestCompareInt64(t *testing.T) {
|
||||
tst.AssertEqual(t, CompareInt64(int64(1), int64(2)), -1)
|
||||
tst.AssertEqual(t, CompareInt64(int64(2), int64(1)), 1)
|
||||
tst.AssertEqual(t, CompareInt64(int64(0), int64(0)), 0)
|
||||
}
|
||||
|
||||
func TestCompareGeneric(t *testing.T) {
|
||||
tst.AssertEqual(t, Compare(1, 2), -1)
|
||||
tst.AssertEqual(t, Compare(2, 1), 1)
|
||||
tst.AssertEqual(t, Compare(2, 2), 0)
|
||||
tst.AssertEqual(t, Compare("x", "y"), -1)
|
||||
tst.AssertEqual(t, Compare(3.5, 1.2), 1)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func floatEquals(a, b, eps float64) bool {
|
||||
return math.Abs(a-b) < eps
|
||||
}
|
||||
|
||||
func TestDegToRadZero(t *testing.T) {
|
||||
if !floatEquals(DegToRad(0), 0, 1e-9) {
|
||||
t.Errorf("expected 0, got %v", DegToRad(0))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDegToRad180(t *testing.T) {
|
||||
if !floatEquals(DegToRad(180), math.Pi, 1e-9) {
|
||||
t.Errorf("expected Pi, got %v", DegToRad(180))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDegToRad90(t *testing.T) {
|
||||
if !floatEquals(DegToRad(90), math.Pi/2, 1e-9) {
|
||||
t.Errorf("expected Pi/2, got %v", DegToRad(90))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRadToDegZero(t *testing.T) {
|
||||
// note: function is implemented as rad / (Pi*180), tests document actual behavior
|
||||
if !floatEquals(RadToDeg(0), 0, 1e-9) {
|
||||
t.Errorf("expected 0, got %v", RadToDeg(0))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoDistanceSamePoint(t *testing.T) {
|
||||
d := GeoDistance(10.0, 50.0, 10.0, 50.0)
|
||||
if !floatEquals(d, 0, 1e-3) {
|
||||
t.Errorf("expected 0, got %v", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoDistancePositive(t *testing.T) {
|
||||
// Berlin (~52.5200, 13.4050) to Munich (~48.1351, 11.5820)
|
||||
d := GeoDistance(13.4050, 52.5200, 11.5820, 48.1351)
|
||||
// Distance should be around ~500km - just check the order of magnitude.
|
||||
if d < 400000 || d > 700000 {
|
||||
t.Errorf("Berlin-Munich distance unexpected: got %v", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoDistanceSymmetric(t *testing.T) {
|
||||
d1 := GeoDistance(10.0, 50.0, 11.0, 51.0)
|
||||
d2 := GeoDistance(11.0, 51.0, 10.0, 50.0)
|
||||
if !floatEquals(d1, d2, 1e-3) {
|
||||
t.Errorf("expected symmetry, got %v != %v", d1, d2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFuncChain(t *testing.T) {
|
||||
addOne := func(v int) int { return v + 1 }
|
||||
timesTwo := func(v int) int { return v * 2 }
|
||||
|
||||
chained := FuncChain(addOne, timesTwo)
|
||||
tst.AssertEqual(t, chained(3), 8)
|
||||
}
|
||||
|
||||
func TestFuncChainOrder(t *testing.T) {
|
||||
first := func(v string) string { return v + "A" }
|
||||
second := func(v string) string { return v + "B" }
|
||||
|
||||
chained := FuncChain(first, second)
|
||||
tst.AssertEqual(t, chained("X"), "XAB")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWriteNopCloserWrite(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
wc := WriteNopCloser(&buf)
|
||||
|
||||
n, err := wc.Write([]byte("hello"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, n, 5)
|
||||
tst.AssertEqual(t, buf.String(), "hello")
|
||||
}
|
||||
|
||||
func TestWriteNopCloserClose(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
wc := WriteNopCloser(&buf)
|
||||
|
||||
err := wc.Close()
|
||||
if err != nil {
|
||||
t.Errorf("expected nil error from no-op Close, got %v", err)
|
||||
}
|
||||
|
||||
// Can still write after close (it's a no-op)
|
||||
_, err = wc.Write([]byte("after"))
|
||||
if err != nil {
|
||||
t.Errorf("expected to write after Close, got %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, buf.String(), "after")
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIterSingleValueSeq(t *testing.T) {
|
||||
seq := IterSingleValueSeq(42)
|
||||
|
||||
count := 0
|
||||
var got int
|
||||
for v := range seq {
|
||||
got = v
|
||||
count++
|
||||
}
|
||||
|
||||
tst.AssertEqual(t, count, 1)
|
||||
tst.AssertEqual(t, got, 42)
|
||||
}
|
||||
|
||||
func TestIterSingleValueSeqString(t *testing.T) {
|
||||
seq := IterSingleValueSeq("hello")
|
||||
|
||||
values := make([]string, 0)
|
||||
for v := range seq {
|
||||
values = append(values, v)
|
||||
}
|
||||
|
||||
tst.AssertEqual(t, len(values), 1)
|
||||
tst.AssertEqual(t, values[0], "hello")
|
||||
}
|
||||
|
||||
func TestIterSingleValueSeq2(t *testing.T) {
|
||||
seq := IterSingleValueSeq2("key", 42)
|
||||
|
||||
count := 0
|
||||
var k string
|
||||
var v int
|
||||
for kk, vv := range seq {
|
||||
k = kk
|
||||
v = vv
|
||||
count++
|
||||
}
|
||||
|
||||
tst.AssertEqual(t, count, 1)
|
||||
tst.AssertEqual(t, k, "key")
|
||||
tst.AssertEqual(t, v, 42)
|
||||
}
|
||||
|
||||
func TestIterSingleValueSeqEarlyBreak(t *testing.T) {
|
||||
seq := IterSingleValueSeq(1)
|
||||
|
||||
count := 0
|
||||
for range seq {
|
||||
count++
|
||||
break
|
||||
}
|
||||
|
||||
tst.AssertEqual(t, count, 1)
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTryPrettyPrintJsonValid(t *testing.T) {
|
||||
in := `{"a":1,"b":2}`
|
||||
out := TryPrettyPrintJson(in)
|
||||
if !strings.Contains(out, "\n") {
|
||||
t.Errorf("expected pretty-printed result with newlines, got %q", out)
|
||||
}
|
||||
if !strings.Contains(out, `"a"`) {
|
||||
t.Errorf("expected key in result, got %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTryPrettyPrintJsonInvalidPassThrough(t *testing.T) {
|
||||
in := `not valid json`
|
||||
tst.AssertEqual(t, TryPrettyPrintJson(in), in)
|
||||
}
|
||||
|
||||
func TestPrettyPrintJsonValid(t *testing.T) {
|
||||
out, ok := PrettyPrintJson(`{"a":1}`)
|
||||
tst.AssertEqual(t, ok, true)
|
||||
if !strings.Contains(out, "\n") {
|
||||
t.Errorf("expected formatted output, got %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrettyPrintJsonInvalid(t *testing.T) {
|
||||
in := `not json`
|
||||
out, ok := PrettyPrintJson(in)
|
||||
tst.AssertEqual(t, ok, false)
|
||||
tst.AssertEqual(t, out, in)
|
||||
}
|
||||
|
||||
func TestPatchJsonString(t *testing.T) {
|
||||
in := `{"a":1,"b":2}`
|
||||
out, err := PatchJson(in, "c", 3)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
var m map[string]any
|
||||
if err := json.Unmarshal([]byte(out), &m); err != nil {
|
||||
t.Fatalf("invalid json result: %v", err)
|
||||
}
|
||||
if v, ok := m["c"].(float64); !ok || v != 3 {
|
||||
t.Errorf("expected c=3, got %v", m["c"])
|
||||
}
|
||||
if v, ok := m["a"].(float64); !ok || v != 1 {
|
||||
t.Errorf("expected a=1, got %v", m["a"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchJsonBytes(t *testing.T) {
|
||||
in := []byte(`{"a":1}`)
|
||||
out, err := PatchJson(in, "b", "hello")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
var m map[string]any
|
||||
if err := json.Unmarshal(out, &m); err != nil {
|
||||
t.Fatalf("invalid json result: %v", err)
|
||||
}
|
||||
if v, ok := m["b"].(string); !ok || v != "hello" {
|
||||
t.Errorf("expected b=hello, got %v", m["b"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchJsonInvalid(t *testing.T) {
|
||||
_, err := PatchJson("not json", "k", "v")
|
||||
if err == nil {
|
||||
t.Errorf("expected error on invalid json")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchRemJson(t *testing.T) {
|
||||
in := `{"a":1,"b":2}`
|
||||
out, err := PatchRemJson(in, "a")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
var m map[string]any
|
||||
if err := json.Unmarshal([]byte(out), &m); err != nil {
|
||||
t.Fatalf("invalid json result: %v", err)
|
||||
}
|
||||
if _, exists := m["a"]; exists {
|
||||
t.Errorf("expected key 'a' to be removed")
|
||||
}
|
||||
if v, ok := m["b"].(float64); !ok || v != 2 {
|
||||
t.Errorf("expected b=2, got %v", m["b"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchRemJsonMissingKey(t *testing.T) {
|
||||
in := `{"a":1}`
|
||||
out, err := PatchRemJson(in, "missing")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
var m map[string]any
|
||||
if err := json.Unmarshal([]byte(out), &m); err != nil {
|
||||
t.Fatalf("invalid json: %v", err)
|
||||
}
|
||||
if v, ok := m["a"].(float64); !ok || v != 1 {
|
||||
t.Errorf("expected a=1, got %v", m["a"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalJsonOrPanic(t *testing.T) {
|
||||
tst.AssertEqual(t, MarshalJsonOrPanic(42), "42")
|
||||
tst.AssertEqual(t, MarshalJsonOrPanic("hi"), `"hi"`)
|
||||
}
|
||||
|
||||
func TestMarshalJsonOrPanicPanics(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("expected panic on un-marshalable input")
|
||||
}
|
||||
}()
|
||||
// channels can't be marshaled
|
||||
MarshalJsonOrPanic(make(chan int))
|
||||
}
|
||||
|
||||
func TestMarshalJsonOrDefault(t *testing.T) {
|
||||
tst.AssertEqual(t, MarshalJsonOrDefault(42, "def"), "42")
|
||||
tst.AssertEqual(t, MarshalJsonOrDefault(make(chan int), "def"), "def")
|
||||
}
|
||||
|
||||
func TestMarshalJsonOrNilSuccess(t *testing.T) {
|
||||
p := MarshalJsonOrNil(42)
|
||||
if p == nil {
|
||||
t.Fatalf("expected non-nil pointer")
|
||||
}
|
||||
tst.AssertEqual(t, *p, "42")
|
||||
}
|
||||
|
||||
func TestMarshalJsonOrNilError(t *testing.T) {
|
||||
p := MarshalJsonOrNil(make(chan int))
|
||||
if p != nil {
|
||||
t.Errorf("expected nil pointer on error, got %v", *p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalJsonIndentOrPanic(t *testing.T) {
|
||||
out := MarshalJsonIndentOrPanic(map[string]int{"a": 1}, "", " ")
|
||||
if !strings.Contains(out, "\n") {
|
||||
t.Errorf("expected indented output, got %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalJsonIndentOrDefault(t *testing.T) {
|
||||
out := MarshalJsonIndentOrDefault(make(chan int), "", " ", "DEF")
|
||||
tst.AssertEqual(t, out, "DEF")
|
||||
}
|
||||
|
||||
func TestMarshalJsonIndentOrNilSuccess(t *testing.T) {
|
||||
p := MarshalJsonIndentOrNil(map[string]int{"a": 1}, "", " ")
|
||||
if p == nil || !strings.Contains(*p, "\n") {
|
||||
t.Errorf("expected indented JSON pointer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalJsonIndentOrNilFailure(t *testing.T) {
|
||||
p := MarshalJsonIndentOrNil(make(chan int), "", " ")
|
||||
if p != nil {
|
||||
t.Errorf("expected nil pointer on error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTypeIsMap(t *testing.T) {
|
||||
h := H{"a": 1}
|
||||
out, err := json.Marshal(h)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, string(out), `{"a":1}`)
|
||||
}
|
||||
|
||||
func TestATypeIsArray(t *testing.T) {
|
||||
a := A{1, "x", true}
|
||||
out, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, string(out), `[1,"x",true]`)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMapKeyArr(t *testing.T) {
|
||||
m := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
keys := MapKeyArr(m)
|
||||
sort.Strings(keys)
|
||||
tst.AssertArrayEqual(t, keys, []string{"a", "b", "c"})
|
||||
}
|
||||
|
||||
func TestMapKeyArrEmpty(t *testing.T) {
|
||||
m := map[string]int{}
|
||||
keys := MapKeyArr(m)
|
||||
tst.AssertEqual(t, len(keys), 0)
|
||||
}
|
||||
|
||||
func TestMapValueArr(t *testing.T) {
|
||||
m := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
values := MapValueArr(m)
|
||||
sort.Ints(values)
|
||||
tst.AssertArrayEqual(t, values, []int{1, 2, 3})
|
||||
}
|
||||
|
||||
func TestArrToMap(t *testing.T) {
|
||||
type item struct {
|
||||
key string
|
||||
v int
|
||||
}
|
||||
arr := []item{{"a", 1}, {"b", 2}}
|
||||
m := ArrToMap(arr, func(i item) string { return i.key })
|
||||
tst.AssertEqual(t, len(m), 2)
|
||||
tst.AssertEqual(t, m["a"].v, 1)
|
||||
tst.AssertEqual(t, m["b"].v, 2)
|
||||
}
|
||||
|
||||
func TestArrToKVMap(t *testing.T) {
|
||||
arr := []int{1, 2, 3}
|
||||
m := ArrToKVMap(arr,
|
||||
func(v int) int { return v },
|
||||
func(v int) string {
|
||||
return [...]string{"", "one", "two", "three"}[v]
|
||||
},
|
||||
)
|
||||
tst.AssertEqual(t, m[1], "one")
|
||||
tst.AssertEqual(t, m[2], "two")
|
||||
tst.AssertEqual(t, m[3], "three")
|
||||
}
|
||||
|
||||
func TestArrToSet(t *testing.T) {
|
||||
arr := []string{"a", "b", "a", "c"}
|
||||
set := ArrToSet(arr)
|
||||
tst.AssertEqual(t, len(set), 3)
|
||||
tst.AssertEqual(t, set["a"], true)
|
||||
tst.AssertEqual(t, set["b"], true)
|
||||
tst.AssertEqual(t, set["c"], true)
|
||||
tst.AssertEqual(t, set["d"], false)
|
||||
}
|
||||
|
||||
func TestMapToArr(t *testing.T) {
|
||||
m := map[string]int{"a": 1, "b": 2}
|
||||
arr := MapToArr(m)
|
||||
tst.AssertEqual(t, len(arr), 2)
|
||||
roundTrip := make(map[string]int)
|
||||
for _, e := range arr {
|
||||
roundTrip[e.Key] = e.Value
|
||||
}
|
||||
tst.AssertEqual(t, roundTrip["a"], 1)
|
||||
tst.AssertEqual(t, roundTrip["b"], 2)
|
||||
}
|
||||
|
||||
func TestCopyMap(t *testing.T) {
|
||||
src := map[string]int{"a": 1, "b": 2}
|
||||
dst := CopyMap(src)
|
||||
tst.AssertEqual(t, len(dst), 2)
|
||||
tst.AssertEqual(t, dst["a"], 1)
|
||||
|
||||
// Mutating dst should not affect src
|
||||
dst["a"] = 99
|
||||
tst.AssertEqual(t, src["a"], 1)
|
||||
}
|
||||
|
||||
func TestForceMapNil(t *testing.T) {
|
||||
var m map[string]int
|
||||
res := ForceMap(m)
|
||||
if res == nil {
|
||||
t.Errorf("expected non-nil result")
|
||||
}
|
||||
tst.AssertEqual(t, len(res), 0)
|
||||
}
|
||||
|
||||
func TestForceMapNonNil(t *testing.T) {
|
||||
m := map[string]int{"x": 1}
|
||||
res := ForceMap(m)
|
||||
tst.AssertEqual(t, res["x"], 1)
|
||||
}
|
||||
|
||||
func TestForceJsonMapOrPanic(t *testing.T) {
|
||||
type s struct {
|
||||
A int `json:"a"`
|
||||
B string `json:"b"`
|
||||
}
|
||||
res := ForceJsonMapOrPanic(s{A: 1, B: "x"})
|
||||
if v, ok := res["a"].(float64); !ok || v != 1 {
|
||||
t.Errorf("expected a=1, got %v", res["a"])
|
||||
}
|
||||
if v, ok := res["b"].(string); !ok || v != "x" {
|
||||
t.Errorf("expected b=x, got %v", res["b"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestForceJsonMapOrPanicPanics(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("expected panic on un-marshalable input")
|
||||
}
|
||||
}()
|
||||
ForceJsonMapOrPanic(make(chan int))
|
||||
}
|
||||
|
||||
func TestMapMerge(t *testing.T) {
|
||||
base := map[string]int{"a": 1, "b": 2}
|
||||
a := map[string]int{"b": 22, "c": 3}
|
||||
b := map[string]int{"d": 4}
|
||||
|
||||
res := MapMerge(base, a, b)
|
||||
tst.AssertEqual(t, res["a"], 1)
|
||||
tst.AssertEqual(t, res["b"], 22) // overwritten
|
||||
tst.AssertEqual(t, res["c"], 3)
|
||||
tst.AssertEqual(t, res["d"], 4)
|
||||
tst.AssertEqual(t, len(res), 4)
|
||||
|
||||
// base must remain untouched
|
||||
tst.AssertEqual(t, base["b"], 2)
|
||||
}
|
||||
|
||||
func TestMapMergeNoExtras(t *testing.T) {
|
||||
base := map[string]int{"a": 1}
|
||||
res := MapMerge(base)
|
||||
tst.AssertEqual(t, res["a"], 1)
|
||||
tst.AssertEqual(t, len(res), 1)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMustSuccess(t *testing.T) {
|
||||
v := Must(42, nil)
|
||||
tst.AssertEqual(t, v, 42)
|
||||
|
||||
s := Must("hello", nil)
|
||||
tst.AssertEqual(t, s, "hello")
|
||||
}
|
||||
|
||||
func TestMustPanics(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("expected panic on error")
|
||||
}
|
||||
}()
|
||||
Must(0, errors.New("boom"))
|
||||
}
|
||||
|
||||
func TestMustBoolSuccess(t *testing.T) {
|
||||
v := MustBool(42, true)
|
||||
tst.AssertEqual(t, v, 42)
|
||||
}
|
||||
|
||||
func TestMustBoolPanics(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("expected panic on not ok")
|
||||
}
|
||||
}()
|
||||
MustBool(0, false)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeepCopyByJsonStruct(t *testing.T) {
|
||||
type item struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
src := item{Name: "alice", Age: 30}
|
||||
dst, err := DeepCopyByJson(src)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, dst.Name, "alice")
|
||||
tst.AssertEqual(t, dst.Age, 30)
|
||||
}
|
||||
|
||||
func TestDeepCopyByJsonSlice(t *testing.T) {
|
||||
src := []int{1, 2, 3}
|
||||
dst, err := DeepCopyByJson(src)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertArrayEqual(t, dst, []int{1, 2, 3})
|
||||
|
||||
// Mutating the copy must not affect the source
|
||||
dst[0] = 99
|
||||
tst.AssertEqual(t, src[0], 1)
|
||||
}
|
||||
|
||||
func TestDeepCopyByJsonError(t *testing.T) {
|
||||
type bad struct {
|
||||
C chan int
|
||||
}
|
||||
_, err := DeepCopyByJson(bad{C: make(chan int)})
|
||||
if err == nil {
|
||||
t.Errorf("expected error for un-marshalable type")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFileExistsTrue(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "f.txt")
|
||||
if err := os.WriteFile(path, []byte("hi"), 0o644); err != nil {
|
||||
t.Fatalf("setup failed: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, FileExists(path), true)
|
||||
}
|
||||
|
||||
func TestFileExistsFalse(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "missing.txt")
|
||||
tst.AssertEqual(t, FileExists(path), false)
|
||||
}
|
||||
|
||||
func TestFileExistsDirectoryReturnsFalse(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
tst.AssertEqual(t, FileExists(dir), false)
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunPanicSafeNoPanic(t *testing.T) {
|
||||
called := false
|
||||
err := RunPanicSafe(func() {
|
||||
called = true
|
||||
})
|
||||
tst.AssertEqual(t, called, true)
|
||||
if err != nil {
|
||||
t.Errorf("expected nil err, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPanicSafeRecovers(t *testing.T) {
|
||||
err := RunPanicSafe(func() {
|
||||
panic("boom")
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatalf("expected error from panic")
|
||||
}
|
||||
pwe, ok := err.(PanicWrappedErr)
|
||||
if !ok {
|
||||
t.Fatalf("expected PanicWrappedErr, got %T", err)
|
||||
}
|
||||
tst.AssertEqual(t, pwe.RecoveredObj(), "boom")
|
||||
tst.AssertEqual(t, pwe.Error(), "A panic occured")
|
||||
}
|
||||
|
||||
func TestRunPanicSafeR1NoPanic(t *testing.T) {
|
||||
expected := errors.New("expected")
|
||||
err := RunPanicSafeR1(func() error {
|
||||
return expected
|
||||
})
|
||||
if err != expected {
|
||||
t.Errorf("expected original error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPanicSafeR1Panics(t *testing.T) {
|
||||
err := RunPanicSafeR1(func() error {
|
||||
panic("boom")
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatalf("expected wrapped panic")
|
||||
}
|
||||
if _, ok := err.(PanicWrappedErr); !ok {
|
||||
t.Errorf("expected PanicWrappedErr, got %T", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPanicSafeR2NoPanic(t *testing.T) {
|
||||
v, err := RunPanicSafeR2(func() (int, error) {
|
||||
return 42, nil
|
||||
})
|
||||
tst.AssertEqual(t, v, 42)
|
||||
if err != nil {
|
||||
t.Errorf("expected nil err, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPanicSafeR2Panics(t *testing.T) {
|
||||
v, err := RunPanicSafeR2(func() (int, error) {
|
||||
panic("boom")
|
||||
})
|
||||
tst.AssertEqual(t, v, 0) // zero value
|
||||
if err == nil {
|
||||
t.Errorf("expected wrapped panic")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPanicSafeR3NoPanic(t *testing.T) {
|
||||
a, b, err := RunPanicSafeR3(func() (int, string, error) {
|
||||
return 1, "two", nil
|
||||
})
|
||||
tst.AssertEqual(t, a, 1)
|
||||
tst.AssertEqual(t, b, "two")
|
||||
if err != nil {
|
||||
t.Errorf("expected nil err, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPanicSafeR3Panics(t *testing.T) {
|
||||
a, b, err := RunPanicSafeR3(func() (int, string, error) {
|
||||
panic("boom")
|
||||
})
|
||||
tst.AssertEqual(t, a, 0)
|
||||
tst.AssertEqual(t, b, "")
|
||||
if err == nil {
|
||||
t.Errorf("expected wrapped panic")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPanicSafeR4NoPanic(t *testing.T) {
|
||||
a, b, c, err := RunPanicSafeR4(func() (int, string, bool, error) {
|
||||
return 1, "two", true, nil
|
||||
})
|
||||
tst.AssertEqual(t, a, 1)
|
||||
tst.AssertEqual(t, b, "two")
|
||||
tst.AssertEqual(t, c, true)
|
||||
if err != nil {
|
||||
t.Errorf("expected nil err, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPanicSafeR4Panics(t *testing.T) {
|
||||
a, b, c, err := RunPanicSafeR4(func() (int, string, bool, error) {
|
||||
panic("boom")
|
||||
})
|
||||
tst.AssertEqual(t, a, 0)
|
||||
tst.AssertEqual(t, b, "")
|
||||
tst.AssertEqual(t, c, false)
|
||||
if err == nil {
|
||||
t.Errorf("expected wrapped panic")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPtr(t *testing.T) {
|
||||
p := Ptr(42)
|
||||
if p == nil {
|
||||
t.Fatalf("expected non-nil")
|
||||
}
|
||||
tst.AssertEqual(t, *p, 42)
|
||||
}
|
||||
|
||||
func TestPtrString(t *testing.T) {
|
||||
p := Ptr("hi")
|
||||
tst.AssertEqual(t, *p, "hi")
|
||||
}
|
||||
|
||||
func TestPTrue(t *testing.T) {
|
||||
if PTrue == nil || *PTrue != true {
|
||||
t.Errorf("PTrue should point to true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPFalse(t *testing.T) {
|
||||
if PFalse == nil || *PFalse != false {
|
||||
t.Errorf("PFalse should point to false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDblPtr(t *testing.T) {
|
||||
pp := DblPtr(7)
|
||||
if pp == nil || *pp == nil {
|
||||
t.Fatalf("expected non-nil double pointer")
|
||||
}
|
||||
tst.AssertEqual(t, **pp, 7)
|
||||
}
|
||||
|
||||
func TestDblPtrIfNotNilWithValue(t *testing.T) {
|
||||
v := 5
|
||||
pp := DblPtrIfNotNil(&v)
|
||||
if pp == nil {
|
||||
t.Fatalf("expected non-nil double pointer")
|
||||
}
|
||||
tst.AssertEqual(t, **pp, 5)
|
||||
}
|
||||
|
||||
func TestDblPtrIfNotNilNil(t *testing.T) {
|
||||
pp := DblPtrIfNotNil[int](nil)
|
||||
if pp != nil {
|
||||
t.Errorf("expected nil for nil input")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDblPtrNil(t *testing.T) {
|
||||
pp := DblPtrNil[int]()
|
||||
if pp == nil {
|
||||
t.Fatalf("expected non-nil outer pointer")
|
||||
}
|
||||
if *pp != nil {
|
||||
t.Errorf("expected inner pointer to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestArrPtr(t *testing.T) {
|
||||
p := ArrPtr(1, 2, 3)
|
||||
if p == nil {
|
||||
t.Fatalf("expected non-nil pointer")
|
||||
}
|
||||
tst.AssertArrayEqual(t, *p, []int{1, 2, 3})
|
||||
}
|
||||
|
||||
func TestPtrInt32(t *testing.T) {
|
||||
p := PtrInt32(7)
|
||||
tst.AssertEqual(t, *p, int32(7))
|
||||
}
|
||||
|
||||
func TestPtrInt64(t *testing.T) {
|
||||
p := PtrInt64(7)
|
||||
tst.AssertEqual(t, *p, int64(7))
|
||||
}
|
||||
|
||||
func TestPtrFloat32(t *testing.T) {
|
||||
p := PtrFloat32(1.5)
|
||||
tst.AssertEqual(t, *p, float32(1.5))
|
||||
}
|
||||
|
||||
func TestPtrFloat64(t *testing.T) {
|
||||
p := PtrFloat64(2.5)
|
||||
tst.AssertEqual(t, *p, 2.5)
|
||||
}
|
||||
|
||||
func TestIsNilTrue(t *testing.T) {
|
||||
tst.AssertEqual(t, IsNil(nil), true)
|
||||
|
||||
var p *int
|
||||
tst.AssertEqual(t, IsNil(p), true)
|
||||
|
||||
var m map[string]int
|
||||
tst.AssertEqual(t, IsNil(m), true)
|
||||
|
||||
var s []int
|
||||
tst.AssertEqual(t, IsNil(s), true)
|
||||
|
||||
var c chan int
|
||||
tst.AssertEqual(t, IsNil(c), true)
|
||||
|
||||
var f func()
|
||||
tst.AssertEqual(t, IsNil(f), true)
|
||||
}
|
||||
|
||||
func TestIsNilFalse(t *testing.T) {
|
||||
v := 5
|
||||
tst.AssertEqual(t, IsNil(&v), false)
|
||||
tst.AssertEqual(t, IsNil(5), false)
|
||||
tst.AssertEqual(t, IsNil("hi"), false)
|
||||
tst.AssertEqual(t, IsNil(map[string]int{}), false)
|
||||
tst.AssertEqual(t, IsNil([]int{}), false)
|
||||
}
|
||||
|
||||
func TestPtrEqualsBothNil(t *testing.T) {
|
||||
tst.AssertEqual(t, PtrEquals[int](nil, nil), true)
|
||||
}
|
||||
|
||||
func TestPtrEqualsBothEqual(t *testing.T) {
|
||||
a := 5
|
||||
b := 5
|
||||
tst.AssertEqual(t, PtrEquals(&a, &b), true)
|
||||
}
|
||||
|
||||
func TestPtrEqualsBothDifferent(t *testing.T) {
|
||||
a := 5
|
||||
b := 6
|
||||
tst.AssertEqual(t, PtrEquals(&a, &b), false)
|
||||
}
|
||||
|
||||
func TestPtrEqualsOneNil(t *testing.T) {
|
||||
a := 5
|
||||
tst.AssertEqual(t, PtrEquals(&a, nil), false)
|
||||
tst.AssertEqual(t, PtrEquals[int](nil, &a), false)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRandBytesLength(t *testing.T) {
|
||||
for _, sz := range []int{0, 1, 16, 32, 1024} {
|
||||
b := RandBytes(sz)
|
||||
tst.AssertEqual(t, len(b), sz)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandBytesDistinct(t *testing.T) {
|
||||
a := RandBytes(32)
|
||||
b := RandBytes(32)
|
||||
|
||||
// Two cryptographic random sequences should not be equal in 32 bytes.
|
||||
if len(a) != 32 || len(b) != 32 {
|
||||
t.Fatalf("unexpected length")
|
||||
}
|
||||
|
||||
equal := true
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
equal = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if equal {
|
||||
t.Errorf("two consecutive 32-byte RandBytes calls returned identical results")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSortInPlace(t *testing.T) {
|
||||
arr := []int{3, 1, 2}
|
||||
Sort(arr)
|
||||
tst.AssertArrayEqual(t, arr, []int{1, 2, 3})
|
||||
}
|
||||
|
||||
func TestSortStrings(t *testing.T) {
|
||||
arr := []string{"c", "a", "b"}
|
||||
Sort(arr)
|
||||
tst.AssertArrayEqual(t, arr, []string{"a", "b", "c"})
|
||||
}
|
||||
|
||||
func TestAsSorted(t *testing.T) {
|
||||
src := []int{3, 1, 2}
|
||||
out := AsSorted(src)
|
||||
tst.AssertArrayEqual(t, out, []int{1, 2, 3})
|
||||
// original unchanged
|
||||
tst.AssertArrayEqual(t, src, []int{3, 1, 2})
|
||||
}
|
||||
|
||||
func TestSortStable(t *testing.T) {
|
||||
arr := []int{3, 1, 2, 1}
|
||||
SortStable(arr)
|
||||
tst.AssertArrayEqual(t, arr, []int{1, 1, 2, 3})
|
||||
}
|
||||
|
||||
func TestAsSortedStable(t *testing.T) {
|
||||
src := []int{3, 1, 2, 1}
|
||||
out := AsSortedStable(src)
|
||||
tst.AssertArrayEqual(t, out, []int{1, 1, 2, 3})
|
||||
tst.AssertArrayEqual(t, src, []int{3, 1, 2, 1})
|
||||
}
|
||||
|
||||
func TestIsSorted(t *testing.T) {
|
||||
tst.AssertEqual(t, IsSorted([]int{1, 2, 3}), true)
|
||||
tst.AssertEqual(t, IsSorted([]int{3, 2, 1}), false)
|
||||
tst.AssertEqual(t, IsSorted([]int{1, 1, 1}), true)
|
||||
tst.AssertEqual(t, IsSorted([]int{}), true)
|
||||
}
|
||||
|
||||
func TestSortSlice(t *testing.T) {
|
||||
arr := []int{3, 1, 2}
|
||||
SortSlice(arr, func(a, b int) bool { return a < b })
|
||||
tst.AssertArrayEqual(t, arr, []int{1, 2, 3})
|
||||
}
|
||||
|
||||
func TestAsSortedSlice(t *testing.T) {
|
||||
src := []int{3, 1, 2}
|
||||
out := AsSortedSlice(src, func(a, b int) bool { return a > b })
|
||||
tst.AssertArrayEqual(t, out, []int{3, 2, 1})
|
||||
tst.AssertArrayEqual(t, src, []int{3, 1, 2})
|
||||
}
|
||||
|
||||
func TestSortSliceStable(t *testing.T) {
|
||||
arr := []int{3, 1, 2, 1}
|
||||
SortSliceStable(arr, func(a, b int) bool { return a < b })
|
||||
tst.AssertArrayEqual(t, arr, []int{1, 1, 2, 3})
|
||||
}
|
||||
|
||||
func TestAsSortedSliceStable(t *testing.T) {
|
||||
src := []int{3, 1, 2, 1}
|
||||
out := AsSortedSliceStable(src, func(a, b int) bool { return a < b })
|
||||
tst.AssertArrayEqual(t, out, []int{1, 1, 2, 3})
|
||||
tst.AssertArrayEqual(t, src, []int{3, 1, 2, 1})
|
||||
}
|
||||
|
||||
func TestIsSliceSorted(t *testing.T) {
|
||||
tst.AssertEqual(t, IsSliceSorted([]int{1, 2, 3}, func(a, b int) bool { return a < b }), true)
|
||||
tst.AssertEqual(t, IsSliceSorted([]int{3, 2, 1}, func(a, b int) bool { return a < b }), false)
|
||||
}
|
||||
|
||||
type byKey struct {
|
||||
key int
|
||||
v string
|
||||
}
|
||||
|
||||
func TestSortBy(t *testing.T) {
|
||||
arr := []byKey{{3, "c"}, {1, "a"}, {2, "b"}}
|
||||
SortBy(arr, func(v byKey) int { return v.key })
|
||||
tst.AssertEqual(t, arr[0].v, "a")
|
||||
tst.AssertEqual(t, arr[1].v, "b")
|
||||
tst.AssertEqual(t, arr[2].v, "c")
|
||||
}
|
||||
|
||||
func TestAsSortedBy(t *testing.T) {
|
||||
src := []byKey{{3, "c"}, {1, "a"}, {2, "b"}}
|
||||
out := AsSortedBy(src, func(v byKey) int { return v.key })
|
||||
tst.AssertEqual(t, out[0].v, "a")
|
||||
tst.AssertEqual(t, out[2].v, "c")
|
||||
// source unchanged
|
||||
tst.AssertEqual(t, src[0].v, "c")
|
||||
}
|
||||
|
||||
func TestSortByStable(t *testing.T) {
|
||||
arr := []byKey{{1, "a1"}, {1, "a2"}, {0, "b"}}
|
||||
SortByStable(arr, func(v byKey) int { return v.key })
|
||||
tst.AssertEqual(t, arr[0].v, "b")
|
||||
// stable order for ties
|
||||
tst.AssertEqual(t, arr[1].v, "a1")
|
||||
tst.AssertEqual(t, arr[2].v, "a2")
|
||||
}
|
||||
|
||||
func TestAsSortedByStable(t *testing.T) {
|
||||
src := []byKey{{1, "a1"}, {1, "a2"}, {0, "b"}}
|
||||
out := AsSortedByStable(src, func(v byKey) int { return v.key })
|
||||
tst.AssertEqual(t, out[0].v, "b")
|
||||
tst.AssertEqual(t, out[1].v, "a1")
|
||||
tst.AssertEqual(t, out[2].v, "a2")
|
||||
// source unchanged
|
||||
tst.AssertEqual(t, src[0].v, "a1")
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package langext
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewUUIDLength(t *testing.T) {
|
||||
u, err := NewUUID()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, len(u), 16)
|
||||
}
|
||||
|
||||
func TestNewUUIDVersionAndVariant(t *testing.T) {
|
||||
u, err := NewUUID()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
// Version 4 is in upper nibble of byte 6
|
||||
tst.AssertEqual(t, u[6]&0xf0, byte(0x40))
|
||||
// Variant 10 in top two bits of byte 8
|
||||
tst.AssertEqual(t, u[8]&0xc0, byte(0x80))
|
||||
}
|
||||
|
||||
func TestNewUUIDRandomness(t *testing.T) {
|
||||
a, _ := NewUUID()
|
||||
b, _ := NewUUID()
|
||||
if a == b {
|
||||
t.Errorf("two UUIDs should not be equal")
|
||||
}
|
||||
}
|
||||
|
||||
var hexUUIDRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`)
|
||||
|
||||
func TestNewHexUUIDFormat(t *testing.T) {
|
||||
s, err := NewHexUUID()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, len(s), 36)
|
||||
if !hexUUIDRegex.MatchString(s) {
|
||||
t.Errorf("not a valid hex UUID format: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustHexUUID(t *testing.T) {
|
||||
s := MustHexUUID()
|
||||
tst.AssertEqual(t, len(s), 36)
|
||||
if !hexUUIDRegex.MatchString(s) {
|
||||
t.Errorf("not a valid hex UUID format: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
var upperHexRegex = regexp.MustCompile(`^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$`)
|
||||
|
||||
func TestNewUpperHexUUIDFormat(t *testing.T) {
|
||||
s, err := NewUpperHexUUID()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, len(s), 36)
|
||||
tst.AssertEqual(t, s, strings.ToUpper(s))
|
||||
if !upperHexRegex.MatchString(s) {
|
||||
t.Errorf("not a valid upper-hex UUID format: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustUpperHexUUID(t *testing.T) {
|
||||
s := MustUpperHexUUID()
|
||||
tst.AssertEqual(t, len(s), 36)
|
||||
}
|
||||
|
||||
func TestNewRawHexUUIDFormat(t *testing.T) {
|
||||
s, err := NewRawHexUUID()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, len(s), 32)
|
||||
if strings.Contains(s, "-") {
|
||||
t.Errorf("raw hex should have no dashes: %q", s)
|
||||
}
|
||||
tst.AssertEqual(t, s, strings.ToUpper(s))
|
||||
}
|
||||
|
||||
func TestMustRawHexUUID(t *testing.T) {
|
||||
s := MustRawHexUUID()
|
||||
tst.AssertEqual(t, len(s), 32)
|
||||
}
|
||||
|
||||
func TestNewBracesUUID(t *testing.T) {
|
||||
s, err := NewBracesUUID()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, len(s), 38)
|
||||
tst.AssertEqual(t, string(s[37]), "}")
|
||||
}
|
||||
|
||||
func TestMustBracesUUID(t *testing.T) {
|
||||
s := MustBracesUUID()
|
||||
tst.AssertEqual(t, len(s), 38)
|
||||
}
|
||||
|
||||
func TestNewParensUUID(t *testing.T) {
|
||||
s, err := NewParensUUID()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
tst.AssertEqual(t, len(s), 38)
|
||||
tst.AssertEqual(t, string(s[37]), ")")
|
||||
}
|
||||
|
||||
func TestMustParensUUID(t *testing.T) {
|
||||
s := MustParensUUID()
|
||||
tst.AssertEqual(t, len(s), 38)
|
||||
}
|
||||
|
||||
func TestUUIDsAreUnique(t *testing.T) {
|
||||
const count = 100
|
||||
seen := make(map[string]bool, count)
|
||||
for range count {
|
||||
s := MustHexUUID()
|
||||
if seen[s] {
|
||||
t.Errorf("collision in UUID set: %q", s)
|
||||
}
|
||||
seen[s] = true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user