| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package chain |
| |
| import ( |
| "context" |
| "errors" |
| "github.com/apache/servicecomb-service-center/pkg/util" |
| "testing" |
| "time" |
| ) |
| |
| const ( |
| times = 1000000 |
| count = 10 |
| ) |
| |
| func init() { |
| for i := 0; i < count; i++ { |
| RegisterHandler("_bench_handlers_", &handler{}) |
| } |
| } |
| |
| type handler struct { |
| } |
| |
| func (h *handler) Handle(i *Invocation) { |
| i.Next() |
| } |
| |
| func syncFunc(i int) { |
| if i >= count { |
| return |
| } |
| syncFunc(i + 1) |
| } |
| |
| func BenchmarkInvocationOption(b *testing.B) { |
| var ( |
| op InvocationOp |
| f = func(r Result) {} |
| opts = []InvocationOption{WithFunc(f), WithAsyncFunc(f)} |
| ) |
| b.RunParallel(func(pb *testing.PB) { |
| for pb.Next() { |
| for _, opt := range opts { |
| op = opt(op) |
| } |
| } |
| }) |
| b.ReportAllocs() |
| // 50000000 26.8 ns/op 0 B/op 0 allocs/op |
| } |
| |
| func BenchmarkChain(b *testing.B) { |
| var ( |
| ctx = util.NewStringContext(context.Background()) |
| f = func(r Result) {} |
| ) |
| |
| b.N = times |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| inv := NewInvocation(ctx, NewChain("_bench_chain_", Handlers("_bench_handlers_"))) |
| inv.Invoke(f) |
| |
| } |
| b.ReportAllocs() |
| // 1000000 735 ns/op 80 B/op 1 allocs/op |
| } |
| |
| type mockHandler struct { |
| } |
| |
| func (h *mockHandler) Handle(i *Invocation) { |
| x := i.Context().Value("x").(int) |
| switch x { |
| case 1: |
| i.Success(x) |
| case 2: |
| i.Fail(errors.New("error"), x) |
| case 3: |
| i.Next(WithFunc(func(r Result) { |
| i.WithContext("x", x*x) |
| })) |
| case 4: |
| i.Next(WithAsyncFunc(func(r Result) { |
| i.WithContext("x", x*x) |
| ch, _ := i.Context().Value("ch").(chan struct{}) |
| ch <- struct{}{} |
| })) |
| case 5: |
| panic(errors.New("error")) |
| case 6: |
| i.Next(WithFunc(func(r Result) { |
| panic(errors.New("error")) |
| })) |
| case 7: |
| i.Next(WithAsyncFunc(func(r Result) { |
| panic(errors.New("error")) |
| })) |
| default: |
| i.WithContext("x", x-1) |
| i.Next() |
| } |
| } |
| |
| func TestChain_Next(t *testing.T) { |
| h := &mockHandler{} |
| hs := Handlers("test") |
| hs = []Handler{h, h, h} |
| |
| x := 0 |
| ch := NewChain("test", hs) |
| if ch.Name() != "test" { |
| t.Fatalf("TestChain_Next") |
| } |
| i := NewInvocation(context.Background(), ch) |
| i.WithContext("x", x) |
| i.Invoke(func(r Result) { |
| if !r.OK || i.Context().Value("x").(int) != -len(hs) { |
| t.Fatalf("TestChain_Next") |
| } |
| }) |
| |
| x = 1 |
| ch = NewChain("test", hs) |
| i = NewInvocation(context.Background(), ch) |
| i.WithContext("x", x) |
| i.Invoke(func(r Result) { |
| if !r.OK || r.Args[0].(int) != x { |
| t.Fatalf("TestChain_Next") |
| } |
| }) |
| |
| x = 2 |
| ch = NewChain("test", hs) |
| i = NewInvocation(context.Background(), ch) |
| i.WithContext("x", x) |
| i.Invoke(func(r Result) { |
| if r.OK || r.Args[0].(int) != x { |
| t.Fatalf("TestChain_Next") |
| } |
| }) |
| |
| x = 3 |
| ch = NewChain("test", hs) |
| i = NewInvocation(context.Background(), ch) |
| i.WithContext("x", x) |
| i.Invoke(func(r Result) { |
| if !r.OK || i.Context().Value("x").(int) != x { |
| t.Fatalf("TestChain_Next") |
| } |
| }) |
| if i.Context().Value("x").(int) != x*x { |
| t.Fatalf("TestChain_Next") |
| } |
| |
| x = 4 // async call back |
| ch = NewChain("test", hs) |
| i = NewInvocation(context.Background(), ch) |
| i.WithContext("x", x) |
| i.WithContext("ch", make(chan struct{})) |
| i.Invoke(func(r Result) { |
| if !r.OK || i.Context().Value("x").(int) != x { |
| t.Fatalf("TestChain_Next") |
| } |
| }) |
| <-i.Context().Value("ch").(chan struct{}) |
| if i.Context().Value("x").(int) != x*x { |
| t.Fatalf("TestChain_Next") |
| } |
| |
| x = 5 |
| ch = NewChain("test", hs) |
| i = NewInvocation(context.Background(), ch) |
| i.WithContext("x", x) |
| i.Invoke(func(r Result) { |
| if r.OK || r.Err == nil { |
| t.Fatalf("TestChain_Next") |
| } |
| }) |
| |
| x = 6 |
| ch = NewChain("test", hs) |
| i = NewInvocation(context.Background(), ch) |
| i.WithContext("x", x) |
| i.Invoke(func(r Result) { |
| if !r.OK || r.Err != nil { |
| t.Fatalf("TestChain_Next") |
| } |
| }) |
| |
| x = 7 // async call back |
| ch = NewChain("test", hs) |
| i = NewInvocation(context.Background(), ch) |
| i.WithContext("x", x) |
| i.Invoke(func(r Result) { |
| if !r.OK || r.Err != nil { |
| t.Fatalf("TestChain_Next") |
| } |
| }) |
| <-time.After(500 * time.Millisecond) |
| } |