diff options
Diffstat (limited to 'internal/warpc/warpc_test.go')
-rw-r--r-- | internal/warpc/warpc_test.go | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/internal/warpc/warpc_test.go b/internal/warpc/warpc_test.go new file mode 100644 index 000000000..3de20a0f4 --- /dev/null +++ b/internal/warpc/warpc_test.go @@ -0,0 +1,439 @@ +package warpc + +import ( + "context" + _ "embed" + "fmt" + "sync" + "sync/atomic" + "testing" + + qt "github.com/frankban/quicktest" +) + +//go:embed wasm/greet.wasm +var greetWasm []byte + +type person struct { + Name string `json:"name"` +} + +func TestKatex(t *testing.T) { + c := qt.New(t) + + opts := Options{ + PoolSize: 8, + Runtime: quickjsBinary, + Main: katexBinary, + } + + d, err := Start[KatexInput, KatexOutput](opts) + c.Assert(err, qt.IsNil) + + defer d.Close() + + ctx := context.Background() + + input := KatexInput{ + Expression: "c = \\pm\\sqrt{a^2 + b^2}", + Options: KatexOptions{ + Output: "html", + DisplayMode: true, + }, + } + + message := Message[KatexInput]{ + Header: Header{ + Version: currentVersion, + ID: uint32(32), + }, + Data: input, + } + + result, err := d.Execute(ctx, message) + c.Assert(err, qt.IsNil) + + c.Assert(result.GetID(), qt.Equals, message.GetID()) +} + +func TestGreet(t *testing.T) { + c := qt.New(t) + opts := Options{ + PoolSize: 1, + Runtime: quickjsBinary, + Main: greetBinary, + Infof: t.Logf, + } + + for i := 0; i < 2; i++ { + func() { + d, err := Start[person, greeting](opts) + if err != nil { + t.Fatal(err) + } + + defer func() { + c.Assert(d.Close(), qt.IsNil) + }() + + ctx := context.Background() + + inputMessage := Message[person]{ + Header: Header{ + Version: currentVersion, + }, + Data: person{ + Name: "Person", + }, + } + + for j := 0; j < 20; j++ { + inputMessage.Header.ID = uint32(j + 1) + g, err := d.Execute(ctx, inputMessage) + if err != nil { + t.Fatal(err) + } + if g.Data.Greeting != "Hello Person!" { + t.Fatalf("got: %v", g) + } + if g.GetID() != inputMessage.GetID() { + t.Fatalf("%d vs %d", g.GetID(), inputMessage.GetID()) + } + } + }() + } +} + +func TestGreetParallel(t *testing.T) { + c := qt.New(t) + + opts := Options{ + Runtime: quickjsBinary, + Main: greetBinary, + PoolSize: 4, + } + d, err := Start[person, greeting](opts) + c.Assert(err, qt.IsNil) + defer func() { + c.Assert(d.Close(), qt.IsNil) + }() + + var wg sync.WaitGroup + + for i := 1; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + ctx := context.Background() + + for j := 0; j < 5; j++ { + base := i * 100 + id := uint32(base + j) + + inputPerson := person{ + Name: fmt.Sprintf("Person %d", id), + } + inputMessage := Message[person]{ + Header: Header{ + Version: currentVersion, + ID: id, + }, + Data: inputPerson, + } + g, err := d.Execute(ctx, inputMessage) + if err != nil { + t.Error(err) + return + } + + c.Assert(g.Data.Greeting, qt.Equals, fmt.Sprintf("Hello Person %d!", id)) + c.Assert(g.GetID(), qt.Equals, inputMessage.GetID()) + + } + }(i) + + } + + wg.Wait() +} + +func TestKatexParallel(t *testing.T) { + c := qt.New(t) + + opts := Options{ + Runtime: quickjsBinary, + Main: katexBinary, + PoolSize: 6, + } + d, err := Start[KatexInput, KatexOutput](opts) + c.Assert(err, qt.IsNil) + defer func() { + c.Assert(d.Close(), qt.IsNil) + }() + + var wg sync.WaitGroup + + for i := 1; i <= 10; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + ctx := context.Background() + + for j := 0; j < 1; j++ { + base := i * 100 + id := uint32(base + j) + + input := katexInputTemplate + inputMessage := Message[KatexInput]{ + Header: Header{ + Version: currentVersion, + ID: id, + }, + Data: input, + } + + result, err := d.Execute(ctx, inputMessage) + if err != nil { + t.Error(err) + return + } + + if result.GetID() != inputMessage.GetID() { + t.Errorf("%d vs %d", result.GetID(), inputMessage.GetID()) + return + } + } + }(i) + + } + + wg.Wait() +} + +func BenchmarkExecuteKatex(b *testing.B) { + opts := Options{ + Runtime: quickjsBinary, + Main: katexBinary, + } + d, err := Start[KatexInput, KatexOutput](opts) + if err != nil { + b.Fatal(err) + } + defer d.Close() + + ctx := context.Background() + + input := katexInputTemplate + + b.ResetTimer() + for i := 0; i < b.N; i++ { + message := Message[KatexInput]{ + Header: Header{ + Version: currentVersion, + ID: uint32(i + 1), + }, + Data: input, + } + + result, err := d.Execute(ctx, message) + if err != nil { + b.Fatal(err) + } + + if result.GetID() != message.GetID() { + b.Fatalf("%d vs %d", result.GetID(), message.GetID()) + } + + } +} + +func BenchmarkKatexStartStop(b *testing.B) { + optsTemplate := Options{ + Runtime: quickjsBinary, + Main: katexBinary, + CompilationCacheDir: b.TempDir(), + } + + runBench := func(b *testing.B, opts Options) { + for i := 0; i < b.N; i++ { + d, err := Start[KatexInput, KatexOutput](opts) + if err != nil { + b.Fatal(err) + } + if err := d.Close(); err != nil { + b.Fatal(err) + } + } + } + + for _, poolSize := range []int{1, 8, 16} { + + name := fmt.Sprintf("PoolSize%d", poolSize) + + b.Run(name, func(b *testing.B) { + opts := optsTemplate + opts.PoolSize = poolSize + runBench(b, opts) + }) + + } +} + +var katexInputTemplate = KatexInput{ + Expression: "c = \\pm\\sqrt{a^2 + b^2}", + Options: KatexOptions{Output: "html", DisplayMode: true}, +} + +func BenchmarkExecuteKatexPara(b *testing.B) { + optsTemplate := Options{ + Runtime: quickjsBinary, + Main: katexBinary, + } + + runBench := func(b *testing.B, opts Options) { + d, err := Start[KatexInput, KatexOutput](opts) + if err != nil { + b.Fatal(err) + } + defer d.Close() + + ctx := context.Background() + + b.ResetTimer() + + var id atomic.Uint32 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + message := Message[KatexInput]{ + Header: Header{ + Version: currentVersion, + ID: id.Add(1), + }, + Data: katexInputTemplate, + } + + result, err := d.Execute(ctx, message) + if err != nil { + b.Fatal(err) + } + if result.GetID() != message.GetID() { + b.Fatalf("%d vs %d", result.GetID(), message.GetID()) + } + } + }) + } + + for _, poolSize := range []int{1, 8, 16} { + name := fmt.Sprintf("PoolSize%d", poolSize) + + b.Run(name, func(b *testing.B) { + opts := optsTemplate + opts.PoolSize = poolSize + runBench(b, opts) + }) + } +} + +func BenchmarkExecuteGreet(b *testing.B) { + opts := Options{ + Runtime: quickjsBinary, + Main: greetBinary, + } + d, err := Start[person, greeting](opts) + if err != nil { + b.Fatal(err) + } + defer d.Close() + + ctx := context.Background() + + input := person{ + Name: "Person", + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + message := Message[person]{ + Header: Header{ + Version: currentVersion, + ID: uint32(i + 1), + }, + Data: input, + } + result, err := d.Execute(ctx, message) + if err != nil { + b.Fatal(err) + } + + if result.GetID() != message.GetID() { + b.Fatalf("%d vs %d", result.GetID(), message.GetID()) + } + + } +} + +func BenchmarkExecuteGreetPara(b *testing.B) { + opts := Options{ + Runtime: quickjsBinary, + Main: greetBinary, + PoolSize: 8, + } + + d, err := Start[person, greeting](opts) + if err != nil { + b.Fatal(err) + } + defer d.Close() + + ctx := context.Background() + + inputTemplate := person{ + Name: "Person", + } + + b.ResetTimer() + + var id atomic.Uint32 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + message := Message[person]{ + Header: Header{ + Version: currentVersion, + ID: id.Add(1), + }, + Data: inputTemplate, + } + + result, err := d.Execute(ctx, message) + if err != nil { + b.Fatal(err) + } + if result.GetID() != message.GetID() { + b.Fatalf("%d vs %d", result.GetID(), message.GetID()) + } + } + }) +} + +type greeting struct { + Greeting string `json:"greeting"` +} + +var ( + greetBinary = Binary{ + Name: "greet", + Data: greetWasm, + } + + katexBinary = Binary{ + Name: "renderkatex", + Data: katexWasm, + } + + quickjsBinary = Binary{ + Name: "javy_quickjs_provider_v2", + Data: quickjsWasm, + } +) |