wa-lang.org/wazero@v1.0.2/experimental/logging/log_listener_test.go (about)

     1  package logging_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	"math"
     8  	"testing"
     9  
    10  	"wa-lang.org/wazero/api"
    11  	"wa-lang.org/wazero/experimental/logging"
    12  	"wa-lang.org/wazero/imports/wasi_snapshot_preview1"
    13  	"wa-lang.org/wazero/internal/testing/require"
    14  	"wa-lang.org/wazero/internal/wasm"
    15  )
    16  
    17  // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
    18  var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
    19  
    20  func Test_loggingListener(t *testing.T) {
    21  	wasiFuncName := "random_get"
    22  	wasiFuncType := &wasm.FunctionType{
    23  		Params:  []api.ValueType{api.ValueTypeI32, api.ValueTypeI32},
    24  		Results: []api.ValueType{api.ValueTypeI32, api.ValueTypeI32},
    25  	}
    26  	wasiParamNames := []string{"buf", "buf_len"}
    27  	wasiParams := []uint64{0, 8}
    28  
    29  	tests := []struct {
    30  		name                 string
    31  		moduleName, funcName string
    32  		functype             *wasm.FunctionType
    33  		isHostFunc           bool
    34  		paramNames           []string
    35  		params, results      []uint64
    36  		err                  error
    37  		expected             string
    38  	}{
    39  		{
    40  			name:     "v_v",
    41  			functype: &wasm.FunctionType{},
    42  			expected: `--> test.fn()
    43  <-- ()
    44  `,
    45  		},
    46  		{
    47  			name:     "error",
    48  			functype: &wasm.FunctionType{},
    49  			err:      io.EOF,
    50  			expected: `--> test.fn()
    51  <-- error: EOF
    52  `,
    53  		},
    54  		{
    55  			name:       "host",
    56  			functype:   &wasm.FunctionType{},
    57  			isHostFunc: true,
    58  			expected: `==> test.fn()
    59  <== ()
    60  `,
    61  		},
    62  		{
    63  			name:       "wasi",
    64  			functype:   wasiFuncType,
    65  			moduleName: wasi_snapshot_preview1.ModuleName,
    66  			funcName:   wasiFuncName,
    67  			paramNames: wasiParamNames,
    68  			isHostFunc: true,
    69  			params:     wasiParams,
    70  			results:    []uint64{uint64(wasi_snapshot_preview1.ErrnoSuccess)},
    71  			expected: `==> wasi_snapshot_preview1.random_get(buf=0,buf_len=8)
    72  <== ESUCCESS
    73  `,
    74  		},
    75  		{
    76  			name:       "wasi errno",
    77  			functype:   wasiFuncType,
    78  			moduleName: wasi_snapshot_preview1.ModuleName,
    79  			funcName:   wasiFuncName,
    80  			paramNames: wasiParamNames,
    81  			isHostFunc: true,
    82  			params:     wasiParams,
    83  			results:    []uint64{uint64(wasi_snapshot_preview1.ErrnoFault)},
    84  			expected: `==> wasi_snapshot_preview1.random_get(buf=0,buf_len=8)
    85  <== EFAULT
    86  `,
    87  		},
    88  		{
    89  			name:       "wasi error",
    90  			functype:   wasiFuncType,
    91  			moduleName: wasi_snapshot_preview1.ModuleName,
    92  			funcName:   wasiFuncName,
    93  			paramNames: wasiParamNames,
    94  			isHostFunc: true,
    95  			params:     wasiParams,
    96  			err:        io.EOF, // not possible as we coerce errors to numbers, but test anyway!
    97  			expected: `==> wasi_snapshot_preview1.random_get(buf=0,buf_len=8)
    98  <== error: EOF
    99  `,
   100  		},
   101  		{
   102  			name:     "i32",
   103  			functype: &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeI32}},
   104  			params:   []uint64{math.MaxUint32},
   105  			expected: `--> test.fn(4294967295)
   106  <-- ()
   107  `,
   108  		},
   109  		{
   110  			name:       "i32 named",
   111  			functype:   &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeI32}},
   112  			params:     []uint64{math.MaxUint32},
   113  			paramNames: []string{"x"},
   114  			expected: `--> test.fn(x=4294967295)
   115  <-- ()
   116  `,
   117  		},
   118  		{
   119  			name:     "i64",
   120  			functype: &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeI64}},
   121  			params:   []uint64{math.MaxUint64},
   122  			expected: `--> test.fn(18446744073709551615)
   123  <-- ()
   124  `,
   125  		},
   126  		{
   127  			name:       "i64 named",
   128  			functype:   &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeI64}},
   129  			params:     []uint64{math.MaxUint64},
   130  			paramNames: []string{"x"},
   131  			expected: `--> test.fn(x=18446744073709551615)
   132  <-- ()
   133  `,
   134  		},
   135  		{
   136  			name:     "f32",
   137  			functype: &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeF32}},
   138  			params:   []uint64{api.EncodeF32(math.MaxFloat32)},
   139  			expected: `--> test.fn(3.4028235e+38)
   140  <-- ()
   141  `,
   142  		},
   143  		{
   144  			name:       "f32 named",
   145  			functype:   &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeF32}},
   146  			params:     []uint64{api.EncodeF32(math.MaxFloat32)},
   147  			paramNames: []string{"x"},
   148  			expected: `--> test.fn(x=3.4028235e+38)
   149  <-- ()
   150  `,
   151  		},
   152  		{
   153  			name:     "f64",
   154  			functype: &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeF64}},
   155  			params:   []uint64{api.EncodeF64(math.MaxFloat64)},
   156  			expected: `--> test.fn(1.7976931348623157e+308)
   157  <-- ()
   158  `,
   159  		},
   160  		{
   161  			name:       "f64 named",
   162  			functype:   &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeF64}},
   163  			params:     []uint64{api.EncodeF64(math.MaxFloat64)},
   164  			paramNames: []string{"x"},
   165  			expected: `--> test.fn(x=1.7976931348623157e+308)
   166  <-- ()
   167  `,
   168  		},
   169  		{
   170  			name:     "externref",
   171  			functype: &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeExternref}},
   172  			params:   []uint64{0},
   173  			expected: `--> test.fn(0000000000000000)
   174  <-- ()
   175  `,
   176  		},
   177  		{
   178  			name:       "externref named",
   179  			functype:   &wasm.FunctionType{Params: []api.ValueType{api.ValueTypeExternref}},
   180  			params:     []uint64{0},
   181  			paramNames: []string{"x"},
   182  			expected: `--> test.fn(x=0000000000000000)
   183  <-- ()
   184  `,
   185  		},
   186  		{
   187  			name:     "v128",
   188  			functype: &wasm.FunctionType{Params: []api.ValueType{0x7b}},
   189  			params:   []uint64{0, 1},
   190  			expected: `--> test.fn(00000000000000000000000000000001)
   191  <-- ()
   192  `,
   193  		},
   194  		{
   195  			name:       "v128 named",
   196  			functype:   &wasm.FunctionType{Params: []api.ValueType{0x7b}},
   197  			params:     []uint64{0, 1},
   198  			paramNames: []string{"x"},
   199  			expected: `--> test.fn(x=00000000000000000000000000000001)
   200  <-- ()
   201  `,
   202  		},
   203  		{
   204  			name:     "funcref",
   205  			functype: &wasm.FunctionType{Params: []api.ValueType{0x70}},
   206  			params:   []uint64{0},
   207  			expected: `--> test.fn(0000000000000000)
   208  <-- ()
   209  `,
   210  		},
   211  		{
   212  			name:       "funcref named",
   213  			functype:   &wasm.FunctionType{Params: []api.ValueType{0x70}},
   214  			params:     []uint64{0},
   215  			paramNames: []string{"x"},
   216  			expected: `--> test.fn(x=0000000000000000)
   217  <-- ()
   218  `,
   219  		},
   220  		{
   221  			name:     "no params, one result",
   222  			functype: &wasm.FunctionType{Results: []api.ValueType{api.ValueTypeI32}},
   223  			results:  []uint64{math.MaxUint32},
   224  			expected: `--> test.fn()
   225  <-- (4294967295)
   226  `,
   227  		},
   228  		{
   229  			name: "one param, one result",
   230  			functype: &wasm.FunctionType{
   231  				Params:  []api.ValueType{api.ValueTypeI32},
   232  				Results: []api.ValueType{api.ValueTypeF32},
   233  			},
   234  			params:  []uint64{math.MaxUint32},
   235  			results: []uint64{api.EncodeF32(math.MaxFloat32)},
   236  			expected: `--> test.fn(4294967295)
   237  <-- (3.4028235e+38)
   238  `,
   239  		},
   240  		{
   241  			name: "two params, two results",
   242  			functype: &wasm.FunctionType{
   243  				Params:  []api.ValueType{api.ValueTypeI32, api.ValueTypeI64},
   244  				Results: []api.ValueType{api.ValueTypeF32, api.ValueTypeF64},
   245  			},
   246  			params:  []uint64{math.MaxUint32, math.MaxUint64},
   247  			results: []uint64{api.EncodeF32(math.MaxFloat32), api.EncodeF64(math.MaxFloat64)},
   248  			expected: `--> test.fn(4294967295,18446744073709551615)
   249  <-- (3.4028235e+38,1.7976931348623157e+308)
   250  `,
   251  		},
   252  		{
   253  			name: "two params, two results named",
   254  			functype: &wasm.FunctionType{
   255  				Params:  []api.ValueType{api.ValueTypeI32, api.ValueTypeI64},
   256  				Results: []api.ValueType{api.ValueTypeF32, api.ValueTypeF64},
   257  			},
   258  			params:     []uint64{math.MaxUint32, math.MaxUint64},
   259  			paramNames: []string{"x", "y"},
   260  			results:    []uint64{api.EncodeF32(math.MaxFloat32), api.EncodeF64(math.MaxFloat64)},
   261  			expected: `--> test.fn(x=4294967295,y=18446744073709551615)
   262  <-- (3.4028235e+38,1.7976931348623157e+308)
   263  `,
   264  		},
   265  	}
   266  
   267  	var out bytes.Buffer
   268  	lf := logging.NewLoggingListenerFactory(&out)
   269  	fn := func() {}
   270  	for _, tt := range tests {
   271  		tc := tt
   272  		t.Run(tc.name, func(t *testing.T) {
   273  			if tc.moduleName == "" {
   274  				tc.moduleName = "test"
   275  			}
   276  			if tc.funcName == "" {
   277  				tc.funcName = "fn"
   278  			}
   279  			m := &wasm.Module{
   280  				TypeSection:     []*wasm.FunctionType{tc.functype},
   281  				FunctionSection: []wasm.Index{0},
   282  				NameSection: &wasm.NameSection{
   283  					ModuleName:    tc.moduleName,
   284  					FunctionNames: wasm.NameMap{{Name: tc.funcName}},
   285  					LocalNames:    wasm.IndirectNameMap{{NameMap: toNameMap(tc.paramNames)}},
   286  				},
   287  			}
   288  
   289  			if tc.isHostFunc {
   290  				m.CodeSection = []*wasm.Code{wasm.MustParseGoReflectFuncCode(fn)}
   291  			} else {
   292  				m.CodeSection = []*wasm.Code{{Body: []byte{wasm.OpcodeEnd}}}
   293  			}
   294  			m.BuildFunctionDefinitions()
   295  			def := m.FunctionDefinitionSection[0]
   296  			l := lf.NewListener(m.FunctionDefinitionSection[0])
   297  
   298  			out.Reset()
   299  			ctx := l.Before(testCtx, def, tc.params)
   300  			l.After(ctx, def, tc.err, tc.results)
   301  			require.Equal(t, tc.expected, out.String())
   302  		})
   303  	}
   304  }
   305  
   306  func toNameMap(names []string) wasm.NameMap {
   307  	if len(names) == 0 {
   308  		return nil
   309  	}
   310  	var ret wasm.NameMap
   311  	for i, n := range names {
   312  		ret = append(ret, &wasm.NameAssoc{Index: wasm.Index(i), Name: n})
   313  	}
   314  	return ret
   315  }
   316  
   317  func Test_loggingListener_indentation(t *testing.T) {
   318  	out := bytes.NewBuffer(nil)
   319  	lf := logging.NewLoggingListenerFactory(out)
   320  	m := &wasm.Module{
   321  		TypeSection:     []*wasm.FunctionType{{}},
   322  		FunctionSection: []wasm.Index{0, 0},
   323  		CodeSection:     []*wasm.Code{{Body: []byte{wasm.OpcodeEnd}}, {Body: []byte{wasm.OpcodeEnd}}},
   324  		NameSection: &wasm.NameSection{
   325  			ModuleName:    "test",
   326  			FunctionNames: wasm.NameMap{{Index: 0, Name: "fn1"}, {Index: 1, Name: "fn2"}},
   327  		},
   328  	}
   329  	m.BuildFunctionDefinitions()
   330  	def1 := m.FunctionDefinitionSection[0]
   331  	l1 := lf.NewListener(def1)
   332  	def2 := m.FunctionDefinitionSection[1]
   333  	l2 := lf.NewListener(def2)
   334  
   335  	ctx := l1.Before(testCtx, def1, []uint64{})
   336  	ctx1 := l2.Before(ctx, def2, []uint64{})
   337  	l2.After(ctx1, def2, nil, []uint64{})
   338  	l1.After(ctx, def1, nil, []uint64{})
   339  	require.Equal(t, `--> test.fn1()
   340  	--> test.fn2()
   341  	<-- ()
   342  <-- ()
   343  `, out.String())
   344  }