initial import
This commit is contained in:
17
core/contextx/deadline.go
Normal file
17
core/contextx/deadline.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package contextx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ShrinkDeadline(ctx context.Context, timeout time.Duration) (context.Context, func()) {
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
leftTime := time.Until(deadline)
|
||||
if leftTime < timeout {
|
||||
timeout = leftTime
|
||||
}
|
||||
}
|
||||
|
||||
return context.WithDeadline(ctx, time.Now().Add(timeout))
|
||||
}
|
||||
27
core/contextx/deadline_test.go
Normal file
27
core/contextx/deadline_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package contextx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShrinkDeadlineLess(t *testing.T) {
|
||||
deadline := time.Now().Add(time.Second)
|
||||
ctx, _ := context.WithDeadline(context.Background(), deadline)
|
||||
ctx, _ = ShrinkDeadline(ctx, time.Minute)
|
||||
dl, ok := ctx.Deadline()
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, deadline, dl)
|
||||
}
|
||||
|
||||
func TestShrinkDeadlineMore(t *testing.T) {
|
||||
deadline := time.Now().Add(time.Minute)
|
||||
ctx, _ := context.WithDeadline(context.Background(), deadline)
|
||||
ctx, _ = ShrinkDeadline(ctx, time.Second)
|
||||
dl, ok := ctx.Deadline()
|
||||
assert.True(t, ok)
|
||||
assert.True(t, dl.Before(deadline))
|
||||
}
|
||||
26
core/contextx/unmarshaler.go
Normal file
26
core/contextx/unmarshaler.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package contextx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"zero/core/mapping"
|
||||
)
|
||||
|
||||
const contextTagKey = "ctx"
|
||||
|
||||
var unmarshaler = mapping.NewUnmarshaler(contextTagKey)
|
||||
|
||||
type contextValuer struct {
|
||||
context.Context
|
||||
}
|
||||
|
||||
func (cv contextValuer) Value(key string) (interface{}, bool) {
|
||||
v := cv.Context.Value(key)
|
||||
return v, v != nil
|
||||
}
|
||||
|
||||
func For(ctx context.Context, v interface{}) error {
|
||||
return unmarshaler.UnmarshalValuer(contextValuer{
|
||||
Context: ctx,
|
||||
}, v)
|
||||
}
|
||||
58
core/contextx/unmarshaler_test.go
Normal file
58
core/contextx/unmarshaler_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package contextx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUnmarshalContext(t *testing.T) {
|
||||
type Person struct {
|
||||
Name string `ctx:"name"`
|
||||
Age int `ctx:"age"`
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, "name", "kevin")
|
||||
ctx = context.WithValue(ctx, "age", 20)
|
||||
|
||||
var person Person
|
||||
err := For(ctx, &person)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "kevin", person.Name)
|
||||
assert.Equal(t, 20, person.Age)
|
||||
}
|
||||
|
||||
func TestUnmarshalContextWithOptional(t *testing.T) {
|
||||
type Person struct {
|
||||
Name string `ctx:"name"`
|
||||
Age int `ctx:"age,optional"`
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, "name", "kevin")
|
||||
|
||||
var person Person
|
||||
err := For(ctx, &person)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "kevin", person.Name)
|
||||
assert.Equal(t, 0, person.Age)
|
||||
}
|
||||
|
||||
func TestUnmarshalContextWithMissing(t *testing.T) {
|
||||
type Person struct {
|
||||
Name string `ctx:"name"`
|
||||
Age int `ctx:"age"`
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, "name", "kevin")
|
||||
|
||||
var person Person
|
||||
err := For(ctx, &person)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
28
core/contextx/valueonlycontext.go
Normal file
28
core/contextx/valueonlycontext.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package contextx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type valueOnlyContext struct {
|
||||
context.Context
|
||||
}
|
||||
|
||||
func (valueOnlyContext) Deadline() (deadline time.Time, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (valueOnlyContext) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (valueOnlyContext) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValueOnlyFrom(ctx context.Context) context.Context {
|
||||
return valueOnlyContext{
|
||||
Context: ctx,
|
||||
}
|
||||
}
|
||||
54
core/contextx/valueonlycontext_test.go
Normal file
54
core/contextx/valueonlycontext_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package contextx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestContextCancel(t *testing.T) {
|
||||
c := context.WithValue(context.Background(), "key", "value")
|
||||
c1, cancel := context.WithCancel(c)
|
||||
o := ValueOnlyFrom(c1)
|
||||
c2, _ := context.WithCancel(o)
|
||||
contexts := []context.Context{c1, c2}
|
||||
|
||||
for _, c := range contexts {
|
||||
assert.NotNil(t, c.Done())
|
||||
assert.Nil(t, c.Err())
|
||||
|
||||
select {
|
||||
case x := <-c.Done():
|
||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
cancel()
|
||||
<-c1.Done()
|
||||
|
||||
assert.Nil(t, o.Err())
|
||||
assert.Equal(t, context.Canceled, c1.Err())
|
||||
assert.NotEqual(t, context.Canceled, c2.Err())
|
||||
}
|
||||
|
||||
func TestConextDeadline(t *testing.T) {
|
||||
c, _ := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
|
||||
o := ValueOnlyFrom(c)
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
case <-o.Done():
|
||||
t.Fatal("ValueOnlyContext: context should not have timed out")
|
||||
}
|
||||
|
||||
c, _ = context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
|
||||
o = ValueOnlyFrom(c)
|
||||
c, _ = context.WithDeadline(o, time.Now().Add(20*time.Millisecond))
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatal("ValueOnlyContext+Deadline: context should have timed out")
|
||||
case <-c.Done():
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user