-
Notifications
You must be signed in to change notification settings - Fork 1
/
BasicTests.cs
234 lines (184 loc) · 6.77 KB
/
BasicTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
using Extism.Sdk.Native;
using Shouldly;
using System.Runtime.InteropServices;
using System.Text;
using Xunit;
namespace Extism.Sdk.Tests;
public class BasicTests
{
[Fact]
public void Alloc()
{
using var plugin = Helpers.LoadPlugin("alloc.wasm");
_ = plugin.Call("run_test", Array.Empty<byte>());
}
[Fact]
public void Fail()
{
using var plugin = Helpers.LoadPlugin("fail.wasm");
Should.Throw<ExtismException>(() => plugin.Call("run_test", Array.Empty<byte>()));
}
[Theory]
[InlineData("abc", 1)]
[InlineData("", 2)]
public void Exit(string code, int expected)
{
using var plugin = Helpers.LoadPlugin("exit.wasm", m =>
{
m.Config["code"] = code;
});
var exception = Should.Throw<ExtismException>(() => plugin.Call("_start", Array.Empty<byte>()));
exception.Message.ShouldContain(expected.ToString());
exception.Message.ShouldContain("WASI exit code");
}
[Fact]
public void Timeout()
{
using var plugin = Helpers.LoadPlugin("sleep.wasm", m =>
{
m.Timeout = TimeSpan.FromMilliseconds(50);
m.Config["duration"] = "3"; // sleep for 3 seconds
});
Should.Throw<ExtismException>(() => plugin.Call("run_test", Array.Empty<byte>()))
.Message.ShouldContain("timeout");
}
[Fact]
public void Cancel()
{
using var plugin = Helpers.LoadPlugin("sleep.wasm", m =>
{
m.Config["duration"] = "1"; // sleep for 1 seconds
});
for (var i = 0; i < 3; i++)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMilliseconds(50));
Should.Throw<ExtismException>(() => plugin.Call("run_test", Array.Empty<byte>(), cts.Token))
.Message.ShouldContain("timeout");
Should.Throw<OperationCanceledException>(() => plugin.Call("run_test", Array.Empty<byte>(), cts.Token));
}
// We should be able to call the plugin normally after a cancellation
plugin.Call("run_test", Array.Empty<byte>());
}
[Fact]
public void FileSystem()
{
using var plugin = Helpers.LoadPlugin("fs.wasm", m =>
{
m.AllowedPaths.Add("data", "/mnt");
});
var output = plugin.Call("run_test", Array.Empty<byte>());
var text = Encoding.UTF8.GetString(output);
text.ShouldBe("hello world!");
}
[Theory]
[InlineData("code.wasm", "count_vowels", true)]
[InlineData("code.wasm", "i_dont_exist", false)]
public void FunctionExists(string fileName, string functionName, bool expected)
{
using var plugin = Helpers.LoadPlugin(fileName);
var actual = plugin.FunctionExists(functionName);
actual.ShouldBe(expected);
}
[Fact]
public void CountHelloWorldVowels()
{
using var plugin = Helpers.LoadPlugin("code.wasm");
var response = plugin.Call("count_vowels", "Hello World");
response.ShouldContain("\"count\":3");
}
[Fact]
public void CountVowelsJson()
{
using var plugin = Helpers.LoadPlugin("code.wasm");
var response = plugin.Call<CountVowelsResponse>("count_vowels", "Hello World");
response.ShouldNotBeNull();
response.Count.ShouldBe(3);
}
[Fact]
public void CountVowelsHostFunctions()
{
for (int i = 0; i < 100; i++)
{
var userData = Marshal.StringToHGlobalAnsi("Hello again!");
using var helloWorld = new HostFunction(
"hello_world",
new[] { ExtismValType.PTR },
new[] { ExtismValType.PTR },
userData,
HelloWorld);
using var plugin = Helpers.LoadPlugin("code-functions.wasm", config: null, helloWorld);
var response = plugin.Call("count_vowels", Encoding.UTF8.GetBytes("Hello World"));
Encoding.UTF8.GetString(response).ShouldBe("{\"count\": 3}");
}
void HelloWorld(CurrentPlugin plugin, Span<ExtismVal> inputs, Span<ExtismVal> outputs)
{
Console.WriteLine("Hello from .NET!");
var text = Marshal.PtrToStringAnsi(plugin.UserData);
Console.WriteLine(text);
var input = plugin.ReadString(new nint(inputs[0].v.ptr));
Console.WriteLine($"Input: {input}");
var output = new string(input); // clone the string
outputs[0].v.ptr = plugin.WriteString(output);
}
}
[Fact]
public void HostFunctionsWithMemory()
{
var userData = Marshal.StringToHGlobalAnsi("Hello again!");
using var helloWorld = HostFunction.FromMethod("to_upper", IntPtr.Zero, (CurrentPlugin plugin, long offset) =>
{
var input = plugin.ReadString(offset);
var output = input.ToUpperInvariant();
Console.WriteLine($"Result: {output}"); ;
plugin.FreeBlock(offset);
return plugin.WriteString(output);
}).WithNamespace("host");
using var plugin = Helpers.LoadPlugin("host_memory.wasm", config: null, helloWorld);
var response = plugin.Call("run_test", Encoding.UTF8.GetBytes("Frodo"));
Encoding.UTF8.GetString(response).ShouldBe("HELLO FRODO!");
}
[Fact]
public void FileLog()
{
var tempFile = Path.GetTempFileName();
Plugin.ConfigureFileLogging(tempFile, LogLevel.Warn);
using (var plugin = Helpers.LoadPlugin("log.wasm"))
{
plugin.Call("run_test", Array.Empty<byte>());
}
// HACK: tempFile gets locked by the Extism runtime
var tempFile2 = Path.GetTempFileName();
File.Copy(tempFile, tempFile2, true);
var content = File.ReadAllText(tempFile2);
content.ShouldContain("warn");
content.ShouldContain("error");
content.ShouldNotContain("info");
content.ShouldNotContain("debug");
content.ShouldNotContain("trace");
}
// [Fact]
// Interferes with FileLog
internal void CustomLog()
{
var builder = new StringBuilder();
Plugin.ConfigureCustomLogging(LogLevel.Warn);
using (var plugin = Helpers.LoadPlugin("log.wasm"))
{
plugin.Call("run_test", Array.Empty<byte>());
}
Plugin.DrainCustomLogs(line => builder.AppendLine(line));
var content = builder.ToString();
content.ShouldContain("warn");
content.ShouldContain("error");
content.ShouldNotContain("info");
content.ShouldNotContain("debug");
content.ShouldNotContain("trace");
}
public class CountVowelsResponse
{
public int Count { get; set; }
public int Total { get; set; }
public string? Vowels { get; set; }
}
}