Skip to content

Commit 77762e1

Browse files
authored
prepare 2.0.0-beta.4 release (#10)
1 parent bb13fc8 commit 77762e1

18 files changed

+807
-56
lines changed

ldlogtest/mock_log.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package ldlogtest
22

33
import (
44
"fmt"
5+
"io"
6+
"os"
57
"regexp"
68
"strings"
79
"sync"
@@ -98,6 +100,30 @@ func (ml *MockLog) AssertMessageMatch(t *testing.T, shouldMatch bool, level ldlo
98100
}
99101
}
100102

103+
// Dump is a shortcut for writing all captured log lines to a Writer.
104+
func (ml *MockLog) Dump(w io.Writer) {
105+
for _, line := range ml.GetAllOutput() {
106+
fmt.Fprintln(w, line.Level.Name()+": "+line.Message)
107+
}
108+
}
109+
110+
// DumpIfTestFailed is a shortcut for writing all captured log lines to standard output only if
111+
// t.Failed() is true.
112+
//
113+
// This is useful in tests where you normally don't want to see the log output, but you do want to see it
114+
// if there was a failure. The simplest way to do this is to use defer:
115+
//
116+
// func TestSomething(t *testing.T) {
117+
// ml := ldlogtest.NewMockLog()
118+
// defer ml.DumpIfTestFailed(t)
119+
// // ... do some test things that may generate log output and/or cause a failure
120+
// }
121+
func (ml *MockLog) DumpIfTestFailed(t *testing.T) {
122+
if t.Failed() { // COVERAGE: there's no way to test this in unit tests
123+
ml.Dump(os.Stdout)
124+
}
125+
}
126+
101127
func (ml *MockLog) logLine(level ldlog.LogLevel, line string) {
102128
ml.lock.Lock()
103129
defer ml.lock.Unlock()

ldlogtest/mock_log_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ldlogtest
22

33
import (
4+
"bytes"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
@@ -74,3 +75,12 @@ func TestMessageMatching(t *testing.T) {
7475
shouldNotMatch(t, ldlog.Info, "third")
7576
shouldNotMatch(t, ldlog.Error, ".")
7677
}
78+
79+
func TestDump(t *testing.T) {
80+
buf := bytes.NewBuffer(nil)
81+
m := NewMockLog()
82+
m.Loggers.Info("first")
83+
m.Loggers.Warn("second")
84+
m.Dump(buf)
85+
assert.Equal(t, "Info: first\nWarn: second\n", string(buf.Bytes()))
86+
}

ldreason/detail.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ type EvaluationDetail struct {
1111
// the default value that was passed to the Variation method.
1212
Value ldvalue.Value
1313
// VariationIndex is the index of the returned value within the flag's list of variations, e.g.
14-
// 0 for the first variation. A negative number indicates that the application default value was
15-
// returned because the flag could not be evaluated.
16-
VariationIndex int
14+
// 0 for the first variation. This is an ldvalue.OptionalInt rather than an int, because it is
15+
// possible for the value to be undefined (there is no variation index if the application default
16+
// value was returned due to an error in evaluation) which is different from a value of 0. See
17+
// ldvalue.OptionalInt for more about how to use this type.
18+
VariationIndex ldvalue.OptionalInt
1719
// Reason is an EvaluationReason object describing the main factor that influenced the flag
1820
// evaluation value.
1921
Reason EvaluationReason
@@ -23,15 +25,21 @@ type EvaluationDetail struct {
2325
// This means that an error prevented the flag from being evaluated; the Reason field should contain
2426
// an error value such as NewEvalReasonError(EvalErrorFlagNotFound).
2527
func (d EvaluationDetail) IsDefaultValue() bool {
26-
return d.VariationIndex < 0
28+
return !d.VariationIndex.IsDefined()
2729
}
2830

29-
// NewEvaluationDetail constructs an EvaluationDeteail, specifying all fields.
30-
func NewEvaluationDetail(value ldvalue.Value, variationIndex int, reason EvaluationReason) EvaluationDetail {
31-
return EvaluationDetail{Value: value, VariationIndex: variationIndex, Reason: reason}
31+
// NewEvaluationDetail constructs an EvaluationDetail, specifying all fields. This assumes that there
32+
// is a defined value for variationIndex; if variationIndex is undefined, use NewEvaluationDetailForError
33+
// or set the struct fields directly.
34+
func NewEvaluationDetail(
35+
value ldvalue.Value,
36+
variationIndex int,
37+
reason EvaluationReason,
38+
) EvaluationDetail {
39+
return EvaluationDetail{Value: value, VariationIndex: ldvalue.NewOptionalInt(variationIndex), Reason: reason}
3240
}
3341

3442
// NewEvaluationDetailForError constructs an EvaluationDetail for an error condition.
3543
func NewEvaluationDetailForError(errorKind EvalErrorKind, defaultValue ldvalue.Value) EvaluationDetail {
36-
return EvaluationDetail{Value: defaultValue, VariationIndex: -1, Reason: NewEvalReasonError(errorKind)}
44+
return EvaluationDetail{Value: defaultValue, Reason: NewEvalReasonError(errorKind)}
3745
}

ldreason/detail_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import (
1111
func TestDetailConstructor(t *testing.T) {
1212
detail := NewEvaluationDetail(ldvalue.Bool(true), 1, NewEvalReasonFallthrough())
1313
assert.Equal(t, ldvalue.Bool(true), detail.Value)
14-
assert.Equal(t, 1, detail.VariationIndex)
14+
assert.Equal(t, ldvalue.NewOptionalInt(1), detail.VariationIndex)
1515
assert.Equal(t, NewEvalReasonFallthrough(), detail.Reason)
1616
assert.False(t, detail.IsDefaultValue())
1717
}
1818

1919
func TestDetailErrorConstructor(t *testing.T) {
2020
detail := NewEvaluationDetailForError(EvalErrorFlagNotFound, ldvalue.Bool(false))
2121
assert.Equal(t, ldvalue.Bool(false), detail.Value)
22-
assert.Equal(t, -1, detail.VariationIndex)
22+
assert.Equal(t, ldvalue.OptionalInt{}, detail.VariationIndex)
2323
assert.Equal(t, NewEvalReasonError(EvalErrorFlagNotFound), detail.Reason)
2424
assert.True(t, detail.IsDefaultValue())
2525
}

lduser/user.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ type User struct {
5757
lastName ldvalue.OptionalString
5858
avatar ldvalue.OptionalString
5959
name ldvalue.OptionalString
60-
anonymous ldvalue.Value
60+
anonymous ldvalue.OptionalBool
6161
custom ldvalue.Value
6262
privateAttributes map[UserAttribute]struct{}
6363
}
@@ -90,7 +90,7 @@ func (u User) GetAttribute(attribute UserAttribute) ldvalue.Value {
9090
case NameAttribute:
9191
return u.name.AsValue()
9292
case AnonymousAttribute:
93-
return u.anonymous
93+
return u.anonymous.AsValue()
9494
default:
9595
value, _ := u.GetCustom(string(attribute))
9696
return value
@@ -157,7 +157,7 @@ func (u User) GetAnonymous() bool {
157157
// GetAnonymousOptional returns the anonymous attribute of the user, with a second value indicating
158158
// whether that attribute was defined for the user or not.
159159
func (u User) GetAnonymousOptional() (bool, bool) {
160-
return u.anonymous.BoolValue(), !u.anonymous.IsNull()
160+
return u.anonymous.Get()
161161
}
162162

163163
// GetCustom returns a custom attribute of the user by name. The boolean second return value indicates
@@ -207,7 +207,7 @@ func (u User) Equal(other User) bool {
207207
u.lastName != other.lastName ||
208208
u.avatar != other.avatar ||
209209
u.name != other.name ||
210-
!u.anonymous.Equal(other.anonymous) {
210+
u.anonymous != other.anonymous {
211211
return false
212212
}
213213
if !u.custom.Equal(other.custom) {

lduser/user_builder.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ func NewUser(key string) User {
99

1010
// NewAnonymousUser creates a new anonymous user identified by the given key.
1111
func NewAnonymousUser(key string) User {
12-
return User{key: key, anonymous: ldvalue.Bool(true)}
12+
return User{key: key, anonymous: ldvalue.NewOptionalBool(true)}
1313
}
1414

1515
// UserBuilder is a mutable object that uses the Builder pattern to specify properties for a User.
@@ -150,7 +150,7 @@ type userBuilderImpl struct {
150150
lastName ldvalue.OptionalString
151151
avatar ldvalue.OptionalString
152152
name ldvalue.OptionalString
153-
anonymous ldvalue.Value
153+
anonymous ldvalue.OptionalBool
154154
custom ldvalue.ObjectBuilder
155155
privateAttrs map[UserAttribute]struct{}
156156
lastAttributeCanMakePrivate UserAttribute
@@ -246,7 +246,7 @@ func (b *userBuilderImpl) Name(value string) UserBuilderCanMakeAttributePrivate
246246
}
247247

248248
func (b *userBuilderImpl) Anonymous(value bool) UserBuilder {
249-
b.anonymous = ldvalue.Bool(value)
249+
b.anonymous = ldvalue.NewOptionalBool(value)
250250
return b
251251
}
252252

@@ -293,8 +293,11 @@ func (b *userBuilderImpl) SetAttribute(
293293
case NameAttribute:
294294
setOptString(&b.name)
295295
case AnonymousAttribute:
296-
if value.IsBool() || value.IsNull() {
297-
b.anonymous = value
296+
switch {
297+
case value.IsNull():
298+
b.anonymous = ldvalue.OptionalBool{}
299+
case value.IsBool():
300+
b.anonymous = ldvalue.NewOptionalBool(value.BoolValue())
298301
}
299302
okPrivate = false
300303
default:

lduser/user_serialization.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type userForDeserialization struct {
3535
LastName ldvalue.OptionalString `json:"lastName"`
3636
Avatar ldvalue.OptionalString `json:"avatar"`
3737
Name ldvalue.OptionalString `json:"name"`
38-
Anonymous ldvalue.Value `json:"anonymous"`
38+
Anonymous ldvalue.OptionalBool `json:"anonymous"`
3939
Custom ldvalue.Value `json:"custom"`
4040
PrivateAttributeNames []UserAttribute `json:"privateAttributeNames"`
4141
}
@@ -122,7 +122,7 @@ func (u User) WriteToJSONBuffer(j *jsonstream.JSONBuffer) {
122122
maybeWriteStringProperty(j, "lastName", u.lastName)
123123
maybeWriteStringProperty(j, "avatar", u.avatar)
124124
maybeWriteStringProperty(j, "name", u.name)
125-
if !u.anonymous.IsNull() {
125+
if u.anonymous.IsDefined() {
126126
j.WriteName("anonymous")
127127
j.WriteBool(u.anonymous.BoolValue())
128128
}

ldvalue/benchmark_data_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package ldvalue
22

33
var (
4+
benchmarkBoolValue = true
5+
benchmarkBoolPointer = &benchmarkBoolValue
6+
benchmarkOptBoolWithValue = NewOptionalBool(benchmarkBoolValue)
7+
benchmarkIntValue = 3333
8+
benchmarkIntPointer = &benchmarkIntValue
9+
benchmarkOptIntWithValue = NewOptionalInt(benchmarkIntValue)
410
benchmarkStringValue = "value"
511
benchmarkStringPointer = &benchmarkStringValue
612
benchmarkOptStringWithValue = NewOptionalString(benchmarkStringValue)
@@ -13,6 +19,8 @@ var (
1319
benchmarkSerializeArrayValue = ArrayOf(String("a"), String("b"), String("c"))
1420
benchmarkSerializeObjectValue = ObjectBuild().Set("a", Int(1)).Set("b", Int(2)).Set("c", Int(3)).Build()
1521

22+
benchmarkOptBoolResult OptionalBool
23+
benchmarkOptIntResult OptionalInt
1624
benchmarkOptStringResult OptionalString
1725
benchmarkStringResult string
1826
benchmarkValueResult Value

ldvalue/constants.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ package ldvalue
22

33
// string literals should be defined here if they are referenced in multiple files
44

5-
const nullAsJSON = "null"
5+
const (
6+
trueString = "true"
7+
falseString = "false"
8+
nullAsJSON = "null"
9+
noneDescription = "[none]"
10+
)
611

712
// ValueType is defined in value_base.go
813

0 commit comments

Comments
 (0)