warpc_test.go - hugo - [fork] hugo port for 9front
 (HTM) git clone https://git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       warpc_test.go (8955B)
       ---
            1 // Copyright 2024 The Hugo Authors. All rights reserved.
            2 //
            3 // Licensed under the Apache License, Version 2.0 (the "License");
            4 // you may not use this file except in compliance with the License.
            5 // You may obtain a copy of the License at
            6 // http://www.apache.org/licenses/LICENSE-2.0
            7 //
            8 // Unless required by applicable law or agreed to in writing, software
            9 // distributed under the License is distributed on an "AS IS" BASIS,
           10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           11 // See the License for the specific language governing permissions and
           12 // limitations under the License.
           13 
           14 package warpc
           15 
           16 import (
           17         "context"
           18         _ "embed"
           19         "fmt"
           20         "sync"
           21         "sync/atomic"
           22         "testing"
           23 
           24         qt "github.com/frankban/quicktest"
           25 )
           26 
           27 //go:embed wasm/greet.wasm
           28 var greetWasm []byte
           29 
           30 type person struct {
           31         Name string `json:"name"`
           32 }
           33 
           34 func TestKatex(t *testing.T) {
           35         c := qt.New(t)
           36 
           37         opts := Options{
           38                 PoolSize: 8,
           39                 Runtime:  quickjsBinary,
           40                 Main:     katexBinary,
           41         }
           42 
           43         d, err := Start[KatexInput, KatexOutput](opts)
           44         c.Assert(err, qt.IsNil)
           45 
           46         defer d.Close()
           47 
           48         runExpression := func(c *qt.C, id uint32, expression string) (Message[KatexOutput], error) {
           49                 c.Helper()
           50 
           51                 ctx := context.Background()
           52 
           53                 input := KatexInput{
           54                         Expression: expression,
           55                         Options: KatexOptions{
           56                                 Output:       "html",
           57                                 DisplayMode:  true,
           58                                 ThrowOnError: true,
           59                         },
           60                 }
           61 
           62                 message := Message[KatexInput]{
           63                         Header: Header{
           64                                 Version: currentVersion,
           65                                 ID:      uint32(id),
           66                         },
           67                         Data: input,
           68                 }
           69 
           70                 return d.Execute(ctx, message)
           71         }
           72 
           73         c.Run("Simple", func(c *qt.C) {
           74                 id := uint32(32)
           75                 result, err := runExpression(c, id, "c = \\pm\\sqrt{a^2 + b^2}")
           76                 c.Assert(err, qt.IsNil)
           77                 c.Assert(result.GetID(), qt.Equals, id)
           78         })
           79 
           80         c.Run("Chemistry", func(c *qt.C) {
           81                 id := uint32(32)
           82                 result, err := runExpression(c, id, "C_p[\\ce{H2O(l)}] = \\pu{75.3 J // mol K}")
           83                 c.Assert(err, qt.IsNil)
           84                 c.Assert(result.GetID(), qt.Equals, id)
           85         })
           86 
           87         c.Run("Invalid expression", func(c *qt.C) {
           88                 id := uint32(32)
           89                 result, err := runExpression(c, id, "c & \\foo\\")
           90                 c.Assert(err, qt.IsNotNil)
           91                 c.Assert(result.GetID(), qt.Equals, id)
           92         })
           93 }
           94 
           95 func TestGreet(t *testing.T) {
           96         c := qt.New(t)
           97         opts := Options{
           98                 PoolSize: 1,
           99                 Runtime:  quickjsBinary,
          100                 Main:     greetBinary,
          101                 Infof:    t.Logf,
          102         }
          103 
          104         for range 2 {
          105                 func() {
          106                         d, err := Start[person, greeting](opts)
          107                         if err != nil {
          108                                 t.Fatal(err)
          109                         }
          110 
          111                         defer func() {
          112                                 c.Assert(d.Close(), qt.IsNil)
          113                         }()
          114 
          115                         ctx := context.Background()
          116 
          117                         inputMessage := Message[person]{
          118                                 Header: Header{
          119                                         Version: currentVersion,
          120                                 },
          121                                 Data: person{
          122                                         Name: "Person",
          123                                 },
          124                         }
          125 
          126                         for j := range 20 {
          127                                 inputMessage.Header.ID = uint32(j + 1)
          128                                 g, err := d.Execute(ctx, inputMessage)
          129                                 if err != nil {
          130                                         t.Fatal(err)
          131                                 }
          132                                 if g.Data.Greeting != "Hello Person!" {
          133                                         t.Fatalf("got: %v", g)
          134                                 }
          135                                 if g.GetID() != inputMessage.GetID() {
          136                                         t.Fatalf("%d vs %d", g.GetID(), inputMessage.GetID())
          137                                 }
          138                         }
          139                 }()
          140         }
          141 }
          142 
          143 func TestGreetParallel(t *testing.T) {
          144         c := qt.New(t)
          145 
          146         opts := Options{
          147                 Runtime:  quickjsBinary,
          148                 Main:     greetBinary,
          149                 PoolSize: 4,
          150         }
          151         d, err := Start[person, greeting](opts)
          152         c.Assert(err, qt.IsNil)
          153         defer func() {
          154                 c.Assert(d.Close(), qt.IsNil)
          155         }()
          156 
          157         var wg sync.WaitGroup
          158 
          159         for i := 1; i <= 10; i++ {
          160                 wg.Add(1)
          161                 go func(i int) {
          162                         defer wg.Done()
          163 
          164                         ctx := context.Background()
          165 
          166                         for j := range 5 {
          167                                 base := i * 100
          168                                 id := uint32(base + j)
          169 
          170                                 inputPerson := person{
          171                                         Name: fmt.Sprintf("Person %d", id),
          172                                 }
          173                                 inputMessage := Message[person]{
          174                                         Header: Header{
          175                                                 Version: currentVersion,
          176                                                 ID:      id,
          177                                         },
          178                                         Data: inputPerson,
          179                                 }
          180                                 g, err := d.Execute(ctx, inputMessage)
          181                                 if err != nil {
          182                                         t.Error(err)
          183                                         return
          184                                 }
          185 
          186                                 c.Assert(g.Data.Greeting, qt.Equals, fmt.Sprintf("Hello Person %d!", id))
          187                                 c.Assert(g.GetID(), qt.Equals, inputMessage.GetID())
          188 
          189                         }
          190                 }(i)
          191 
          192         }
          193 
          194         wg.Wait()
          195 }
          196 
          197 func TestKatexParallel(t *testing.T) {
          198         c := qt.New(t)
          199 
          200         opts := Options{
          201                 Runtime:  quickjsBinary,
          202                 Main:     katexBinary,
          203                 PoolSize: 6,
          204         }
          205         d, err := Start[KatexInput, KatexOutput](opts)
          206         c.Assert(err, qt.IsNil)
          207         defer func() {
          208                 c.Assert(d.Close(), qt.IsNil)
          209         }()
          210 
          211         var wg sync.WaitGroup
          212 
          213         for i := 1; i <= 10; i++ {
          214                 wg.Add(1)
          215                 go func(i int) {
          216                         defer wg.Done()
          217 
          218                         ctx := context.Background()
          219 
          220                         for j := range 1 {
          221                                 base := i * 100
          222                                 id := uint32(base + j)
          223 
          224                                 input := katexInputTemplate
          225                                 inputMessage := Message[KatexInput]{
          226                                         Header: Header{
          227                                                 Version: currentVersion,
          228                                                 ID:      id,
          229                                         },
          230                                         Data: input,
          231                                 }
          232 
          233                                 result, err := d.Execute(ctx, inputMessage)
          234                                 if err != nil {
          235                                         t.Error(err)
          236                                         return
          237                                 }
          238 
          239                                 if result.GetID() != inputMessage.GetID() {
          240                                         t.Errorf("%d vs %d", result.GetID(), inputMessage.GetID())
          241                                         return
          242                                 }
          243                         }
          244                 }(i)
          245 
          246         }
          247 
          248         wg.Wait()
          249 }
          250 
          251 func BenchmarkExecuteKatex(b *testing.B) {
          252         opts := Options{
          253                 Runtime: quickjsBinary,
          254                 Main:    katexBinary,
          255         }
          256         d, err := Start[KatexInput, KatexOutput](opts)
          257         if err != nil {
          258                 b.Fatal(err)
          259         }
          260         defer d.Close()
          261 
          262         ctx := context.Background()
          263 
          264         input := katexInputTemplate
          265 
          266         b.ResetTimer()
          267         for i := 0; i < b.N; i++ {
          268                 message := Message[KatexInput]{
          269                         Header: Header{
          270                                 Version: currentVersion,
          271                                 ID:      uint32(i + 1),
          272                         },
          273                         Data: input,
          274                 }
          275 
          276                 result, err := d.Execute(ctx, message)
          277                 if err != nil {
          278                         b.Fatal(err)
          279                 }
          280 
          281                 if result.GetID() != message.GetID() {
          282                         b.Fatalf("%d vs %d", result.GetID(), message.GetID())
          283                 }
          284 
          285         }
          286 }
          287 
          288 func BenchmarkKatexStartStop(b *testing.B) {
          289         optsTemplate := Options{
          290                 Runtime:             quickjsBinary,
          291                 Main:                katexBinary,
          292                 CompilationCacheDir: b.TempDir(),
          293         }
          294 
          295         runBench := func(b *testing.B, opts Options) {
          296                 for i := 0; i < b.N; i++ {
          297                         d, err := Start[KatexInput, KatexOutput](opts)
          298                         if err != nil {
          299                                 b.Fatal(err)
          300                         }
          301                         if err := d.Close(); err != nil {
          302                                 b.Fatal(err)
          303                         }
          304                 }
          305         }
          306 
          307         for _, poolSize := range []int{1, 8, 16} {
          308 
          309                 name := fmt.Sprintf("PoolSize%d", poolSize)
          310 
          311                 b.Run(name, func(b *testing.B) {
          312                         opts := optsTemplate
          313                         opts.PoolSize = poolSize
          314                         runBench(b, opts)
          315                 })
          316 
          317         }
          318 }
          319 
          320 var katexInputTemplate = KatexInput{
          321         Expression: "c = \\pm\\sqrt{a^2 + b^2}",
          322         Options:    KatexOptions{Output: "html", DisplayMode: true},
          323 }
          324 
          325 func BenchmarkExecuteKatexPara(b *testing.B) {
          326         optsTemplate := Options{
          327                 Runtime: quickjsBinary,
          328                 Main:    katexBinary,
          329         }
          330 
          331         runBench := func(b *testing.B, opts Options) {
          332                 d, err := Start[KatexInput, KatexOutput](opts)
          333                 if err != nil {
          334                         b.Fatal(err)
          335                 }
          336                 defer d.Close()
          337 
          338                 ctx := context.Background()
          339 
          340                 b.ResetTimer()
          341 
          342                 var id atomic.Uint32
          343                 b.RunParallel(func(pb *testing.PB) {
          344                         for pb.Next() {
          345                                 message := Message[KatexInput]{
          346                                         Header: Header{
          347                                                 Version: currentVersion,
          348                                                 ID:      id.Add(1),
          349                                         },
          350                                         Data: katexInputTemplate,
          351                                 }
          352 
          353                                 result, err := d.Execute(ctx, message)
          354                                 if err != nil {
          355                                         b.Fatal(err)
          356                                 }
          357                                 if result.GetID() != message.GetID() {
          358                                         b.Fatalf("%d vs %d", result.GetID(), message.GetID())
          359                                 }
          360                         }
          361                 })
          362         }
          363 
          364         for _, poolSize := range []int{1, 8, 16} {
          365                 name := fmt.Sprintf("PoolSize%d", poolSize)
          366 
          367                 b.Run(name, func(b *testing.B) {
          368                         opts := optsTemplate
          369                         opts.PoolSize = poolSize
          370                         runBench(b, opts)
          371                 })
          372         }
          373 }
          374 
          375 func BenchmarkExecuteGreet(b *testing.B) {
          376         opts := Options{
          377                 Runtime: quickjsBinary,
          378                 Main:    greetBinary,
          379         }
          380         d, err := Start[person, greeting](opts)
          381         if err != nil {
          382                 b.Fatal(err)
          383         }
          384         defer d.Close()
          385 
          386         ctx := context.Background()
          387 
          388         input := person{
          389                 Name: "Person",
          390         }
          391 
          392         b.ResetTimer()
          393         for i := 0; i < b.N; i++ {
          394                 message := Message[person]{
          395                         Header: Header{
          396                                 Version: currentVersion,
          397                                 ID:      uint32(i + 1),
          398                         },
          399                         Data: input,
          400                 }
          401                 result, err := d.Execute(ctx, message)
          402                 if err != nil {
          403                         b.Fatal(err)
          404                 }
          405 
          406                 if result.GetID() != message.GetID() {
          407                         b.Fatalf("%d vs %d", result.GetID(), message.GetID())
          408                 }
          409 
          410         }
          411 }
          412 
          413 func BenchmarkExecuteGreetPara(b *testing.B) {
          414         opts := Options{
          415                 Runtime:  quickjsBinary,
          416                 Main:     greetBinary,
          417                 PoolSize: 8,
          418         }
          419 
          420         d, err := Start[person, greeting](opts)
          421         if err != nil {
          422                 b.Fatal(err)
          423         }
          424         defer d.Close()
          425 
          426         ctx := context.Background()
          427 
          428         inputTemplate := person{
          429                 Name: "Person",
          430         }
          431 
          432         b.ResetTimer()
          433 
          434         var id atomic.Uint32
          435         b.RunParallel(func(pb *testing.PB) {
          436                 for pb.Next() {
          437                         message := Message[person]{
          438                                 Header: Header{
          439                                         Version: currentVersion,
          440                                         ID:      id.Add(1),
          441                                 },
          442                                 Data: inputTemplate,
          443                         }
          444 
          445                         result, err := d.Execute(ctx, message)
          446                         if err != nil {
          447                                 b.Fatal(err)
          448                         }
          449                         if result.GetID() != message.GetID() {
          450                                 b.Fatalf("%d vs %d", result.GetID(), message.GetID())
          451                         }
          452                 }
          453         })
          454 }
          455 
          456 type greeting struct {
          457         Greeting string `json:"greeting"`
          458 }
          459 
          460 var (
          461         greetBinary = Binary{
          462                 Name: "greet",
          463                 Data: greetWasm,
          464         }
          465 
          466         katexBinary = Binary{
          467                 Name: "renderkatex",
          468                 Data: katexWasm,
          469         }
          470 
          471         quickjsBinary = Binary{
          472                 Name: "javy_quickjs_provider_v2",
          473                 Data: quickjsWasm,
          474         }
          475 )