Skip to content

Commit 83aadd6

Browse files
ilonatommymaraf
andauthored
[browser] tests and fixes for heap larger than 2GB (#104662)
* Add wbt * Test all cases, including workaround. * Remove workaround case from wbt. * Clean up temporary changes. * Fix typo Co-authored-by: Marek Fišera <[email protected]> --------- Co-authored-by: Marek Fišera <[email protected]>
1 parent a15b123 commit 83aadd6

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

eng/testing/scenarios/BuildWasmAppsJobsList.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Wasm.Build.Tests.TestAppScenarios.LazyLoadingTests
3939
Wasm.Build.Tests.TestAppScenarios.LibraryInitializerTests
4040
Wasm.Build.Tests.TestAppScenarios.SatelliteLoadingTests
4141
Wasm.Build.Tests.TestAppScenarios.ModuleConfigTests
42+
Wasm.Build.Tests.TestAppScenarios.MemoryTests
4243
Wasm.Build.Tests.AspNetCore.SignalRClientTests
4344
Wasm.Build.Tests.WasmBuildAppTest
4445
Wasm.Build.Tests.WasmNativeDefaultsTests
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
using Xunit.Abstractions;
9+
using Xunit;
10+
11+
#nullable enable
12+
13+
namespace Wasm.Build.Tests.TestAppScenarios;
14+
15+
public class MemoryTests : AppTestBase
16+
{
17+
public MemoryTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
18+
: base(output, buildContext)
19+
{
20+
}
21+
22+
[Theory]
23+
[InlineData("Release", true)]
24+
[InlineData("Release", false)]
25+
public async Task AllocateLargeHeapThenRepeatedlyInterop(string config, bool buildNative)
26+
{
27+
// native build triggers passing value form EmccMaximumHeapSize to MAXIMUM_MEMORY that is set in emscripten
28+
// in non-native build EmccMaximumHeapSize does not have an effect, so the test will fail with "out of memory"
29+
CopyTestAsset("WasmBasicTestApp", "MemoryTests", "App");
30+
string extraArgs = $"-p:EmccMaximumHeapSize=4294901760 -p:WasmBuildNative={buildNative}";
31+
BuildProject(config, assertAppBundle: false, extraArgs: extraArgs);
32+
33+
var result = await RunSdkStyleAppForBuild(new (Configuration: config, TestScenario: "AllocateLargeHeapThenInterop", ExpectedExitCode: buildNative ? 0 : 1));
34+
if (!buildNative)
35+
Assert.Contains(result.TestOutput, item => item.Contains("Exception System.OutOfMemoryException: Out of memory"));
36+
}
37+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Text.Json;
7+
using System.Text;
8+
using System.Runtime.InteropServices.JavaScript;
9+
10+
public partial class MemoryTest // ?test=AllocateLargeHeapThenInterop
11+
{
12+
[JSImport("countChars", "main.js")]
13+
internal static partial int CountChars(string testArray);
14+
15+
[JSExport]
16+
internal static void Run()
17+
{
18+
// Allocate over 2GB space, 2 621 440 000 bytes
19+
const int arrayCnt = 25;
20+
int[][] arrayHolder = new int[arrayCnt][];
21+
string errors = "";
22+
TestOutput.WriteLine("Starting over 2GB array allocation");
23+
for (int i = 0; i < arrayCnt; i++)
24+
{
25+
try
26+
{
27+
arrayHolder[i] = new int[1024 * 1024 * 25];
28+
}
29+
catch (Exception ex)
30+
{
31+
errors += $"Exception {ex} was thrown on i={i}";
32+
}
33+
}
34+
TestOutput.WriteLine("Finished over 2GB array allocation");
35+
36+
// call a method many times to trigger tier-up optimization
37+
string randomString = GenerateRandomString(1000);
38+
try
39+
{
40+
for (int i = 0; i < 10000; i++)
41+
{
42+
int count = CountChars(randomString);
43+
if (count != randomString.Length)
44+
errors += $"CountChars returned {count} instead of {randomString.Length} for {i}-th string.";
45+
}
46+
}
47+
catch (Exception ex)
48+
{
49+
errors += $"Exception {ex} was thrown when CountChars was called in a loop";
50+
}
51+
if (!string.IsNullOrEmpty(errors))
52+
{
53+
TestOutput.WriteLine(errors);
54+
throw new Exception(errors);
55+
}
56+
else
57+
{
58+
TestOutput.WriteLine("Great success, MemoryTest finished without errors.");
59+
}
60+
}
61+
62+
private static Random random = new Random();
63+
64+
private static string GenerateRandomString(int stringLength)
65+
{
66+
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
67+
var stringBuilder = new StringBuilder(stringLength);
68+
for (int i = 0; i < stringLength; i++)
69+
{
70+
stringBuilder.Append(chars[random.Next(chars.Length)]);
71+
}
72+
return stringBuilder.ToString();
73+
}
74+
}

src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ function testOutput(msg) {
1414
console.log(`TestOutput -> ${msg}`);
1515
}
1616

17+
function countChars(str) {
18+
const length = str.length;
19+
testOutput(`JS received str of ${length} length`);
20+
return length;
21+
}
22+
1723
// Prepare base runtime parameters
1824
dotnet
1925
.withElementOnExit()
@@ -168,6 +174,13 @@ try {
168174
case "MaxParallelDownloads":
169175
exit(0);
170176
break;
177+
case "AllocateLargeHeapThenInterop":
178+
setModuleImports('main.js', {
179+
countChars
180+
});
181+
exports.MemoryTest.Run();
182+
exit(0);
183+
break;
171184
default:
172185
console.error(`Unknown test case: ${testCase}`);
173186
exit(3);

0 commit comments

Comments
 (0)