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 )