Skip to content

Commit

Permalink
Add tests and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
pellared committed Jan 19, 2024
1 parent 9fdb679 commit 0380851
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 67 deletions.
67 changes: 0 additions & 67 deletions log/keyvalue.go

This file was deleted.

59 changes: 59 additions & 0 deletions log/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,62 @@ func (v Value) append(dst []byte) []byte {
panic(fmt.Sprintf("bad kind: %s", v.Kind()))
}
}

// An KeyValue is a key-value pair.
type KeyValue struct {
Key string
Value Value
}

// String returns an KeyValue for a string value.
func String(key, value string) KeyValue {
return KeyValue{key, StringValue(value)}
}

// Int64 returns an KeyValue for an int64.
func Int64(key string, value int64) KeyValue {
return KeyValue{key, Int64Value(value)}
}

// Int converts an int to an int64 and returns
// an KeyValue with that value.
func Int(key string, value int) KeyValue {
return Int64(key, int64(value))
}

// Uint64 returns an KeyValue for a uint64.
func Uint64(key string, v uint64) KeyValue {
return KeyValue{key, Uint64Value(v)}
}

// Float64 returns an KeyValue for a floating-point number.
func Float64(key string, v float64) KeyValue {
return KeyValue{key, Float64Value(v)}
}

// Bool returns an KeyValue for a bool.
func Bool(key string, v bool) KeyValue {
return KeyValue{key, BoolValue(v)}
}

// Group returns an KeyValue for a Group [Value].
//
// Use Group to collect several key-value pairs under a single
// key.
func Group(key string, args ...KeyValue) KeyValue {
return KeyValue{key, GroupValue(args...)}
}

// Invalid reports whether the key-value has empty key or value.
func (a KeyValue) Invalid() bool {
return a.Key == "" || a.Value.Empty()
}

// Equal reports whether a and b have equal keys and values.
func (a KeyValue) Equal(b KeyValue) bool {
return a.Key == b.Key && a.Value.Equal(b.Value)
}

func (a KeyValue) String() string {
return fmt.Sprintf("%s=%s", a.Key, a.Value)
}
163 changes: 163 additions & 0 deletions log/value_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package log

import (
"fmt"
"testing"
"time"
"unsafe"

"github.com/stretchr/testify/assert"
)

func TestKindString(t *testing.T) {
got := KindGroup.String()
assert.Equal(t, "Group", got)
}

func TestValueEqual(t *testing.T) {
vals := []Value{
{},
Int64Value(1),
Int64Value(2),
Float64Value(3.5),
Float64Value(3.7),
BoolValue(true),
BoolValue(false),
GroupValue(Bool("b", true), Int("i", 3)),
}
for i, v1 := range vals {
for j, v2 := range vals {
got := v1.Equal(v2)
want := i == j
if got != want {
t.Errorf("%v.Equal(%v): got %t, want %t", v1, v2, got, want)
}
}
}
}

func TestValueString(t *testing.T) {
for _, test := range []struct {
v Value
want string
}{
{Int64Value(-3), "-3"},
{Uint64Value(1), "1"},
{Float64Value(.15), "0.15"},
{BoolValue(true), "true"},
{StringValue("foo"), "foo"},
{GroupValue(Int("a", 1), Bool("b", true)), "[a=1 b=true]"},
} {
got := test.v.String()
assert.Equal(t, test.want, got)
}
}

func TestValueNoAlloc(t *testing.T) {
// Assign values just to make sure the compiler doesn't optimize away the statements.
var (
i int64
u uint64
f float64
b bool
s string
x any
tm time.Time
)
a := int(testing.AllocsPerRun(5, func() {
i = Int64Value(1).Int64()
u = Uint64Value(1).Uint64()
f = Float64Value(1).Float64()
b = BoolValue(true).Bool()
s = StringValue("foo").String()
}))
assert.Zero(t, a)
_ = i
_ = u
_ = f
_ = b
_ = s
_ = x
_ = tm
}

func TestKeyValueNoAlloc(t *testing.T) {
// Assign values just to make sure the compiler doesn't optimize away the statements.
var (
i int64
u uint64
f float64
b bool
s string
x any
)
a := int(testing.AllocsPerRun(5, func() {
i = Int64("key", 1).Value.Int64()
u = Uint64("key", 1).Value.Uint64()
f = Float64("key", 1).Value.Float64()
b = Bool("key", true).Value.Bool()
s = String("key", "foo").Value.String()
}))
assert.Zero(t, a)
_ = i
_ = u
_ = f
_ = b
_ = s
_ = x
}

func TestValueAny(t *testing.T) {
for _, test := range []struct {
want any
in Value
}{
{"s", StringValue("s")},
{true, BoolValue(true)},
{int64(4), IntValue(4)},
{uint64(2), Uint64Value(2)},
{int64(11), Int64Value(11)},
{1.5, Float64Value(1.5)},
{[]KeyValue{Int("i", 3)}, GroupValue(Int("i", 3))},
} {
got := test.in.Any()
assert.Equal(t, test.want, got)
}
}

func TestEmptyGroup(t *testing.T) {
g := GroupValue(
Int("a", 1),
Group("g1", Group("g2")),
Group("g3", Group("g4", Int("b", 2))))
got := g.Group()
want := []KeyValue{Int("a", 1), Group("g3", Group("g4", Int("b", 2)))}
assert.Equal(t, want, got)
}

// A Value with "unsafe" strings is significantly faster:
// safe: 1785 ns/op, 0 allocs
// unsafe: 690 ns/op, 0 allocs

// Run this with and without -tags unsafe_kvs to compare.
func BenchmarkUnsafeStrings(b *testing.B) {
b.ReportAllocs()
dst := make([]Value, 100)
src := make([]Value, len(dst))
b.Logf("Value size = %d", unsafe.Sizeof(Value{}))
for i := range src {
src[i] = StringValue(fmt.Sprintf("string#%d", i))
}
b.ResetTimer()
var d string
for i := 0; i < b.N; i++ {
copy(dst, src)
for _, a := range dst {
d = a.String()
}
}
_ = d
}

0 comments on commit 0380851

Please sign in to comment.