-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRuntimeApiAndErrorTests.cs
More file actions
166 lines (137 loc) · 6.2 KB
/
Copy pathRuntimeApiAndErrorTests.cs
File metadata and controls
166 lines (137 loc) · 6.2 KB
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
using AuroraScript.Runtime;
using AuroraScript.Runtime.Types;
using AuroraScript.Tests.Infrastructure;
using System;
using System.Threading.Tasks;
using Xunit;
namespace AuroraScript.Tests;
public sealed class RuntimeApiAndErrorTests
{
[Fact]
public void CreateDomainBeforeBuildFailsClearly()
{
using var workspace = new TestWorkspace();
var engine = workspace.CreateEngine();
var error = Assert.Throws<AuroraException>(() => engine.CreateDomain());
Assert.Contains("has not been built", error.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task ExecuteReportsMissingModuleMethodAndNonFunction()
{
using var workspace = new TestWorkspace();
var (_, domain) = await workspace.CompileModuleAsync(
"@module(TEST); export var value = 1; export func run() { return 1; }");
Assert.Contains("module", Assert.Throws<AuroraException>(() => domain.Execute("MISSING", "run")).Message, StringComparison.OrdinalIgnoreCase);
Assert.Contains("does not exist", Assert.Throws<AuroraException>(() => domain.Execute("TEST", "missing")).Message, StringComparison.OrdinalIgnoreCase);
Assert.Contains("not a valid script method", Assert.Throws<AuroraException>(() => domain.Execute("TEST", "value")).Message, StringComparison.OrdinalIgnoreCase);
Assert.Null(domain.GetMethod("MISSING", "run"));
Assert.Null(domain.GetMethod("TEST", "missing"));
Assert.Same(ScriptObject.Null, domain.GetModule("MISSING"));
}
[Fact]
public async Task UnhandledThrowProducesRuntimeExceptionWithScriptFrame()
{
using var workspace = new TestWorkspace();
var (_, domain) = await workspace.CompileModuleAsync(
"""
@module(TEST);
func inner() { throw new Error('release failure'); }
export func outer() { return inner(); }
""");
var error = Assert.Throws<AuroraRuntimeException>(() => TestWorkspace.Execute(domain, "outer"));
Assert.Contains("release failure", error.Message, StringComparison.Ordinal);
Assert.NotNull(error.StackTrace);
Assert.Contains(error.StackTrace, frame => frame.Method.Contains("inner", StringComparison.Ordinal));
}
[Theory]
[InlineData("var value = null; return value.name;", "Cannot read")]
[InlineData("var value = null; value.name = 1; return value;", "Cannot")]
[InlineData("var value = 1; return value();", "called")]
public void InvalidRuntimeOperationsThrowAuroraRuntimeException(string body, string message)
{
using var workspace = new TestWorkspace();
var engine = workspace.CreateEngine();
var block = engine.CompileBlock(body);
var error = Assert.Throws<AuroraRuntimeException>(() => block.Invoke(Array.Empty<ScriptDatum>()));
Assert.Contains(message, error.ToString(), StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task ConstModulePropertyCannotBeReassignedAtCompileTime()
{
using var workspace = new TestWorkspace();
var error = await Assert.ThrowsAsync<AuroraCompilationException>(() => workspace.CompileModuleAsync(
"@module(TEST); export const value = 1; export func mutate() { value = 2; }"));
Assert.Contains("Cannot assign to constant 'value'", error.Message);
}
[Fact]
public async Task UserStateIsVisibleThroughStateAndCanDifferPerDomain()
{
using var workspace = new TestWorkspace();
var engine = workspace.CreateEngine();
await engine.BuildAsync(workspace.MemorySource(
"main.as",
"@module(TEST); export func read() { return $state.Value; }"));
var firstState = new ScriptObject();
firstState.Define("Value", ScriptDatum.FromNumber(10));
var secondState = new ScriptObject();
secondState.Define("Value", ScriptDatum.FromNumber(20));
var first = engine.CreateDomain(userState: firstState);
var second = engine.CreateDomain(userState: secondState);
ScriptAssert.Equal(10, first.Execute("TEST", "read"));
ScriptAssert.Equal(20, second.Execute("TEST", "read"));
}
[Fact]
public async Task UserStateCanDefineScriptDatumPropertiesDirectly()
{
using var workspace = new TestWorkspace();
var engine = workspace.CreateEngine();
await engine.BuildAsync(workspace.MemorySource(
"main.as",
"""
@module(TEST);
export func run() {
return [$state.Name, $state.Count, $state.Enabled, $state.Empty];
}
"""));
var userState = new ScriptObject();
userState.Define("Name", ScriptDatum.FromString("datum"));
userState.Define("Count", ScriptDatum.FromNumber(3));
userState.Define("Enabled", ScriptDatum.FromBoolean(true));
userState.Define("Empty", ScriptDatum.Null);
var domain = engine.CreateDomain(userState: userState);
ScriptAssert.Equal(new object?[] { "datum", 3, true, null }, domain.Execute("TEST", "run"));
}
[Fact]
public async Task UserStateFallsBackToClrMethods()
{
using var workspace = new TestWorkspace();
var engine = workspace.CreateEngine();
await engine.BuildAsync(workspace.MemorySource(
"main.as",
"""
@module(TEST);
export func run() {
return [$state.Add(20, 22), $state.Title];
}
"""));
var domain = engine.CreateDomain(userState: new MethodState());
ScriptAssert.Equal(new object?[] { 42, "state" }, domain.Execute("TEST", "run"));
}
[Fact]
public async Task DisposedDomainNoLongerExposesModules()
{
using var workspace = new TestWorkspace();
var (_, domain) = await workspace.CompileModuleAsync("@module(TEST); export func run() { return 1; }");
domain.Dispose();
Assert.Throws<AuroraException>(() => TestWorkspace.Execute(domain, "run"));
}
public sealed class HostState
{
public int Value { get; set; }
}
private sealed class MethodState : ScriptObject
{
public string Title => "state";
public int Add(int left, int right) => left + right;
}
}