From c0ebcc8fda275097104a78933fd4cc6e8978a65d Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Fri, 25 Oct 2024 07:41:53 -0400 Subject: [PATCH] Test support in wasm --- BeefLibs/corlib/src/Internal.bf | 92 +++++++++++++++++++++++++++++ BeefTools/WasmLaunch/src/Program.bf | 22 +++++-- IDE/src/BuildContext.bf | 2 +- IDE/src/IDEApp.bf | 25 +++++++- 4 files changed, 132 insertions(+), 9 deletions(-) diff --git a/BeefLibs/corlib/src/Internal.bf b/BeefLibs/corlib/src/Internal.bf index 22854d23..5b5518a5 100644 --- a/BeefLibs/corlib/src/Internal.bf +++ b/BeefLibs/corlib/src/Internal.bf @@ -1,5 +1,6 @@ using System.Collections; using System.Reflection; +using System.Diagnostics; namespace System { @@ -98,6 +99,96 @@ namespace System [Intrinsic("returnaddress")] public static extern void* GetReturnAddress(int32 level = 0); +#if BF_PLATFORM_WASM + static int32 sTestIdx; + class TestEntry + { + public String mName ~ delete _; + public String mFilePath ~ delete _; + public int mLine; + public int mColumn; + public bool mShouldFail; + public bool mProfile; + public bool mIgnore; + public bool mFailed; + public bool mExecuted; + } + static List sTestEntries ~ DeleteContainerAndItems!(_); + + [CallingConvention(.Cdecl), LinkName("Test_Init_Wasm")] + static void Test_Init(char8* testData) + { + sTestEntries = new .(); + + for (var cmd in StringView(testData).Split('\n')) + { + List cmdParts = scope .(cmd.Split('\t')); + let attribs = cmdParts[1]; + + TestEntry testEntry = new TestEntry(); + testEntry.mName = new String(cmdParts[0]); + testEntry.mFilePath = new String(cmdParts[2]); + testEntry.mLine = int32.Parse(cmdParts[3]).Get(); + testEntry.mColumn = int32.Parse(cmdParts[4]).Get(); + List attributes = scope .(attribs.Split('\a')); + for(var i in attributes) + { + if(i.StartsWith('\v')) + { + if(i == "Sf") + testEntry.mShouldFail = true; + else if(i == "Pr") + testEntry.mProfile = true; + else if(i == "Ig") + testEntry.mIgnore = true; + } + else if(i.StartsWith("Name")) + { + testEntry.mName.Clear(); + scope String(i.Substring("Name".Length)).Escape(testEntry.mName); + } + } + sTestEntries.Add(testEntry); + } + } + + [CallingConvention(.Cdecl), LinkName("Test_Error_Wasm")] + static void Test_Error(char8* error) + { + Debug.WriteLine(scope $"TEST ERROR: {StringView(error)}"); + } + + [CallingConvention(.Cdecl), LinkName("Test_Write_Wasm")] + static void Test_Write(char8* str) + { + Debug.Write(StringView(str)); + } + + [CallingConvention(.Cdecl), LinkName("Test_Query_Wasm")] + static int32 Test_Query() + { + while (sTestIdx < sTestEntries.Count) + { + var testEntry = sTestEntries[sTestIdx]; + if ((testEntry.mIgnore) || (testEntry.mShouldFail)) + { + sTestIdx++; + continue; + } + + Debug.WriteLine($"Test '{testEntry.mName}'"); + break; + } + + return sTestIdx++; + } + + [CallingConvention(.Cdecl), LinkName("Test_Finish_Wasm")] + static void Test_Finish() + { + Debug.WriteLine("Tests done."); + } +#else [CallingConvention(.Cdecl)] static extern void Test_Init(char8* testData); [CallingConvention(.Cdecl)] @@ -108,6 +199,7 @@ namespace System static extern int32 Test_Query(); [CallingConvention(.Cdecl)] static extern void Test_Finish(); +#endif static void* sModuleHandle; [AlwaysInclude] diff --git a/BeefTools/WasmLaunch/src/Program.bf b/BeefTools/WasmLaunch/src/Program.bf index 88d82f73..9cb90054 100644 --- a/BeefTools/WasmLaunch/src/Program.bf +++ b/BeefTools/WasmLaunch/src/Program.bf @@ -36,18 +36,19 @@ class Program process.Kill(); } - static void WaitForPort(int32 port) + static bool WaitForPort(int32 port, int32 maxWaitTime) { Stopwatch sw = scope .(); sw.Start(); Socket.Init(); - while (sw.ElapsedMilliseconds < 5000) + while (sw.ElapsedMilliseconds < maxWaitTime) { var socket = scope Socket(); if (socket.Connect("127.0.0.1", port, var sockaddr) case .Ok) - return; + return true; } + return false; } public static void OpenURL(StringView url) @@ -109,13 +110,24 @@ class Program ProcessStartInfo procInfo = scope ProcessStartInfo(); procInfo.UseShellExecute = false; procInfo.SetFileName(serverPath); - procInfo.SetArguments(scope $"-p {port} {htmlDir}"); + procInfo.SetArguments(scope $"-p {port} \"{htmlDir}\""); procInfo.CreateNoWindow = true; var process = scope SpawnedProcess(); process.Start(procInfo).IgnoreError(); - WaitForPort(port); + Stopwatch sw = scope .()..Start(); + while (true) + { + if (WaitForPort(port, 1000)) + break; + if ((process.WaitFor(0)) || (sw.ElapsedMilliseconds >= 15*1000)) + { + Windows.MessageBoxA(default, scope $"Failed to start miniserve on port {port}", "WASM LAUNCH ERROR", Windows.MB_ICONHAND | Windows.MB_OK); + return 1; + } + } + OpenURL(scope $"http://127.0.0.1:{port}/{htmlFileName}"); while (true) diff --git a/IDE/src/BuildContext.bf b/IDE/src/BuildContext.bf index 6390b8aa..1f592353 100644 --- a/IDE/src/BuildContext.bf +++ b/IDE/src/BuildContext.bf @@ -1432,7 +1432,7 @@ namespace IDE String outputDir = scope String(); String absOutputDir = scope String(); - if (testProjectInfo != null) + if ((testProjectInfo != null) && (mPlatformType != .Wasm)) { absOutputDir.Append(projectBuildDir); outputDir = absOutputDir; diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index db6428c4..a2b91aff 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -614,6 +614,15 @@ namespace IDE } } + public Workspace.PlatformType CurrentPlatform + { + get + { + Workspace.Options workspaceOptions = GetCurWorkspaceOptions(); + return Workspace.PlatformType.GetFromName(mPlatformName, workspaceOptions.mTargetTriple); + } + } + [CallingConvention(.Stdcall), CLink] static extern void IDEHelper_ProgramStart(); [CallingConvention(.Stdcall), CLink] @@ -5864,6 +5873,15 @@ namespace IDE [IDECommand] protected void RunTests(bool includeIgnored, bool debug) { + var workspaceOptions = GetCurWorkspaceOptions(); + if (CurrentPlatform == .Wasm) + { + if (workspaceOptions.mBuildKind != .Test) + mMainFrame.mStatusBar.SelectConfig("Test"); + CompileAndRun(true); + return; + } + if (mOutputPanel != null) { ShowPanel(mOutputPanel, false); @@ -5884,7 +5902,6 @@ namespace IDE String prevConfigName = scope String(mConfigName); - var workspaceOptions = GetCurWorkspaceOptions(); if (workspaceOptions.mBuildKind != .Test) { mMainFrame.mStatusBar.SelectConfig("Test"); @@ -5898,10 +5915,12 @@ namespace IDE return; } + var platformType = Workspace.PlatformType.GetFromName(gApp.mPlatformName, workspaceOptions.mTargetTriple); + mLastTestFailed = false; mTestManager = new TestManager(); mTestManager.mPrevConfigName = new String(prevConfigName); - mTestManager.mDebug = debug; + mTestManager.mDebug = debug && (platformType != .Wasm); mTestManager.mIncludeIgnored = includeIgnored; if (mOutputPanel != null) @@ -12093,7 +12112,7 @@ namespace IDE if ((compileKind == .RunAfter) || (compileKind == .DebugAfter)) { - if (workspaceOptions.mBuildKind == .Test) + if ((workspaceOptions.mBuildKind == .Test) && (platform != .Wasm)) { OutputErrorLine("Cannot directly run Test workspace configurations. Use the 'Test' menu to run or debug tests."); return false;