1 package wasm
2
3 import (
4 "context"
5 "fmt"
6 "testing"
7
8 "github.com/tetratelabs/wazero/internal/testing/require"
9 )
10
11 func TestStore_registerModule(t *testing.T) {
12 s := newStore()
13 m1 := &ModuleInstance{ModuleName: "m1"}
14
15 t.Run("adds module", func(t *testing.T) {
16 require.NoError(t, s.registerModule(m1))
17 require.Equal(t, map[string]*ModuleInstance{m1.ModuleName: m1}, s.nameToModule)
18 require.Equal(t, m1, s.moduleList)
19 require.Equal(t, nameToModuleShrinkThreshold, s.nameToModuleCap)
20 })
21
22 t.Run("adds second module", func(t *testing.T) {
23 m2 := &ModuleInstance{ModuleName: "m2"}
24 require.NoError(t, s.registerModule(m2))
25 require.Equal(t, map[string]*ModuleInstance{m1.ModuleName: m1, m2.ModuleName: m2}, s.nameToModule)
26 require.Equal(t, m2, s.moduleList)
27 require.Equal(t, nameToModuleShrinkThreshold, s.nameToModuleCap)
28 })
29
30 t.Run("error on duplicated non anonymous", func(t *testing.T) {
31 m1Second := &ModuleInstance{ModuleName: "m1"}
32 require.EqualError(t, s.registerModule(m1Second), "module[m1] has already been instantiated")
33 })
34
35 t.Run("error on closed", func(t *testing.T) {
36 require.NoError(t, s.CloseWithExitCode(context.Background(), 0))
37 require.Error(t, s.registerModule(m1))
38 })
39 }
40
41 func TestStore_deleteModule(t *testing.T) {
42 s, m1, m2 := newTestStore()
43
44 t.Run("delete one module", func(t *testing.T) {
45 require.NoError(t, s.deleteModule(m2))
46
47
48 require.Equal(t, map[string]*ModuleInstance{m1.ModuleName: m1}, s.nameToModule)
49 require.Equal(t, m1, s.moduleList)
50 require.Equal(t, nameToModuleShrinkThreshold, s.nameToModuleCap)
51 })
52
53 t.Run("ok if missing", func(t *testing.T) {
54 require.NoError(t, s.deleteModule(m2))
55 })
56
57 t.Run("delete last module", func(t *testing.T) {
58 require.NoError(t, s.deleteModule(m1))
59
60 require.Zero(t, len(s.nameToModule))
61 require.Nil(t, s.moduleList)
62 require.Equal(t, nameToModuleShrinkThreshold, s.nameToModuleCap)
63 })
64
65 t.Run("delete middle", func(t *testing.T) {
66 s := newStore()
67 one, two, three := &ModuleInstance{ModuleName: "1"}, &ModuleInstance{ModuleName: "2"}, &ModuleInstance{ModuleName: "3"}
68 require.NoError(t, s.registerModule(one))
69 require.NoError(t, s.registerModule(two))
70 require.NoError(t, s.registerModule(three))
71 require.Equal(t, three, s.moduleList)
72 require.Nil(t, three.prev)
73 require.Equal(t, two, three.next)
74 require.Equal(t, two.prev, three)
75 require.Equal(t, one, two.next)
76 require.Equal(t, one.prev, two)
77 require.Nil(t, one.next)
78 require.NoError(t, s.deleteModule(two))
79 require.Equal(t, three, s.moduleList)
80 require.Nil(t, three.prev)
81 require.Equal(t, one, three.next)
82 require.Equal(t, one.prev, three)
83 require.Nil(t, one.next)
84 })
85 }
86
87 func TestStore_module(t *testing.T) {
88 s, m1, _ := newTestStore()
89
90 t.Run("ok", func(t *testing.T) {
91 got, err := s.module(m1.ModuleName)
92 require.NoError(t, err)
93 require.Equal(t, m1, got)
94 })
95
96 t.Run("unknown", func(t *testing.T) {
97 got, err := s.module("unknown")
98 require.Error(t, err)
99 require.Nil(t, got)
100 })
101
102 t.Run("store closed", func(t *testing.T) {
103 require.NoError(t, s.CloseWithExitCode(context.Background(), 0))
104 got, err := s.module(m1.ModuleName)
105 require.Error(t, err)
106 require.Nil(t, got)
107 })
108 }
109
110 func TestStore_nameToModuleCap(t *testing.T) {
111 t.Run("nameToModuleCap grows beyond initial cap", func(t *testing.T) {
112 s := newStore()
113 for i := 0; i < 300; i++ {
114 require.NoError(t, s.registerModule(&ModuleInstance{ModuleName: fmt.Sprintf("m%d", i)}))
115 }
116
117 require.Equal(t, 300, s.nameToModuleCap)
118 })
119
120 t.Run("nameToModuleCap shrinks by half the cap", func(t *testing.T) {
121 s := newStore()
122 for i := 0; i < 400; i++ {
123 require.NoError(t, s.registerModule(&ModuleInstance{ModuleName: fmt.Sprintf("m%d", i)}))
124 }
125
126 for i := 0; i < 250; i++ {
127 require.NoError(t, s.deleteModule(s.nameToModule[fmt.Sprintf("m%d", i)]))
128 }
129
130 require.Equal(t, 200, s.nameToModuleCap)
131 })
132
133 t.Run("nameToModuleCap does not shrink below initial size", func(t *testing.T) {
134 s := newStore()
135 for i := 0; i < 400; i++ {
136 require.NoError(t, s.registerModule(&ModuleInstance{ModuleName: fmt.Sprintf("m%d", i)}))
137 }
138
139 for i := 0; i < 350; i++ {
140 require.NoError(t, s.deleteModule(s.nameToModule[fmt.Sprintf("m%d", i)]))
141 }
142
143 require.Equal(t, nameToModuleShrinkThreshold, s.nameToModuleCap)
144 })
145
146 t.Run("nameToModuleCap does not grow when if nameToModule does not grow", func(t *testing.T) {
147 s := newStore()
148 for i := 0; i < 99; i++ {
149 require.NoError(t, s.registerModule(&ModuleInstance{ModuleName: fmt.Sprintf("m%d", i)}))
150 }
151 for i := 0; i < 400; i++ {
152 require.NoError(t, s.registerModule(&ModuleInstance{ModuleName: fmt.Sprintf("m%d", i+99)}))
153 require.NoError(t, s.deleteModule(s.nameToModule[fmt.Sprintf("m%d", i+99)]))
154 require.Equal(t, nameToModuleShrinkThreshold, s.nameToModuleCap)
155 }
156 })
157 }
158
159 func TestStore_Module(t *testing.T) {
160 s, m1, _ := newTestStore()
161
162 t.Run("ok", func(t *testing.T) {
163 require.Equal(t, m1, s.Module(m1.ModuleName))
164 })
165
166 t.Run("unknown", func(t *testing.T) {
167 require.Nil(t, s.Module("unknown"))
168 })
169 }
170
171
172 func newTestStore() (*Store, *ModuleInstance, *ModuleInstance) {
173 s := newStore()
174 m1 := &ModuleInstance{ModuleName: "m1"}
175 m2 := &ModuleInstance{ModuleName: "m2"}
176
177 m1.prev = m2
178 m2.next = m1
179 s.nameToModule = map[string]*ModuleInstance{m1.ModuleName: m1, m2.ModuleName: m2}
180 s.moduleList = m2
181 return s, m1, m2
182 }
183
View as plain text