From 0aef88d2a3b66620c1d70694bc3d124f2096d323 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 28 May 2026 21:38:03 +0200 Subject: [PATCH 1/9] chore: upgrade xunit from v2 to v3 Replace the deprecated xunit v2 (`XUnit` 2.9.3) package with `xunit.v3` 3.2.2 in the Beam, Php, Python and Rust test projects. v3 test projects are self-executing, so each project now sets `OutputType=Exe` (added to Rust and Php; Python and Beam already had it) and the .NET-side `[]` is dropped in favour of the runner entry point generated by xunit.v3: - Rust `main.fs` is also transpiled to Rust's `fn main`, so its entry point is guarded with `#if FABLE_COMPILER` rather than removed. - Python/Beam/Php had the entry point only in the .NET (`#else`) branch of `Main.fs`, which is removed. `xunit.runner.visualstudio` 3.1.5 is already v3-compatible, so `dotnet test` continues to work. Test sources need no changes: `Xunit.FactAttribute`, `Assert.Equal` and `Assert.NotEqual` are unchanged in v3. Verified with `dotnet test -c Release`: Rust (2449), Python (2266) and Beam (2439) pass. The Php project has pre-existing compile errors on .NET unrelated to xunit and is not run by the build system. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/Beam/Fable.Tests.Beam.fsproj | 2 +- tests/Beam/Main.fs | 4 +++- tests/Php/Fable.Tests.Php.fsproj | 2 +- tests/Php/Main.fs | 4 +++- tests/Python/Fable.Tests.Python.fsproj | 2 +- tests/Python/Main.fs | 4 +++- tests/Rust/Fable.Tests.Rust.fsproj | 3 ++- tests/Rust/tests/src/lib.fs | 6 +++++- 8 files changed, 19 insertions(+), 8 deletions(-) diff --git a/tests/Beam/Fable.Tests.Beam.fsproj b/tests/Beam/Fable.Tests.Beam.fsproj index 54bd8eb912..e79318100e 100644 --- a/tests/Beam/Fable.Tests.Beam.fsproj +++ b/tests/Beam/Fable.Tests.Beam.fsproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Beam/Main.fs b/tests/Beam/Main.fs index b00af3eaac..7cfe91c15e 100644 --- a/tests/Beam/Main.fs +++ b/tests/Beam/Main.fs @@ -2,5 +2,7 @@ module Program () #else -module Program = let [] main _ = 0 +module Program +// On .NET, xunit.v3 generates the test runner entry point automatically. +() #endif diff --git a/tests/Php/Fable.Tests.Php.fsproj b/tests/Php/Fable.Tests.Php.fsproj index 509b2813e4..ec49f9e604 100644 --- a/tests/Php/Fable.Tests.Php.fsproj +++ b/tests/Php/Fable.Tests.Php.fsproj @@ -9,7 +9,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Php/Main.fs b/tests/Php/Main.fs index ae5f6f6959..d54c0fa690 100644 --- a/tests/Php/Main.fs +++ b/tests/Php/Main.fs @@ -2,5 +2,7 @@ module Program () #else -module Program = let [] main _ = 0 +module Program +// On .NET, xunit.v3 generates the test runner entry point automatically. +() #endif \ No newline at end of file diff --git a/tests/Python/Fable.Tests.Python.fsproj b/tests/Python/Fable.Tests.Python.fsproj index 24ef0fbcf9..79f2f1dc52 100644 --- a/tests/Python/Fable.Tests.Python.fsproj +++ b/tests/Python/Fable.Tests.Python.fsproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Python/Main.fs b/tests/Python/Main.fs index ae5f6f6959..d54c0fa690 100644 --- a/tests/Python/Main.fs +++ b/tests/Python/Main.fs @@ -2,5 +2,7 @@ module Program () #else -module Program = let [] main _ = 0 +module Program +// On .NET, xunit.v3 generates the test runner entry point automatically. +() #endif \ No newline at end of file diff --git a/tests/Rust/Fable.Tests.Rust.fsproj b/tests/Rust/Fable.Tests.Rust.fsproj index 2b9a48b8ba..d266d4d06c 100644 --- a/tests/Rust/Fable.Tests.Rust.fsproj +++ b/tests/Rust/Fable.Tests.Rust.fsproj @@ -1,5 +1,6 @@ + Exe net10.0 Major false @@ -8,7 +9,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Rust/tests/src/lib.fs b/tests/Rust/tests/src/lib.fs index e448cd8f1f..f9b2027a20 100644 --- a/tests/Rust/tests/src/lib.fs +++ b/tests/Rust/tests/src/lib.fs @@ -6,6 +6,10 @@ let _imports() = importAll "./ExtInteropTests.rs" () -// Comment the main func when no longer needed (after migrating to xunit v3) +// [] +#if FABLE_COMPILER +// On .NET, xunit.v3 generates the test runner entry point; only emit our own +// entry point when transpiling (Rust needs a `fn main`). [] let main _args = 0 +#endif From 26e15fce01347d3a7da42ae72f03b93fca6de9bc Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 28 May 2026 22:06:17 +0200 Subject: [PATCH 2/9] chore: stop xunit.v3 attaching runner sources to the Fable build CI revealed that the Fable transpilation of the Rust tests failed with FS0433 ("EntryPointAttribute must be the last declaration in the last file"). When Fable cracks a project it defines FABLE_COMPILER and runs the CoreCompile target, which triggers xunit.v3's _XunitAttachSourceFiles target. That appends a generated entry point and the built-in runner reporters after the project's own source files. For Rust this collides with the `[]` in tests/src/main.fs (and those sources also reference runtime types Fable cannot transpile). Disable both attached source files for the Fable build only, keyed off the FABLE_COMPILER MSBuild property that Fable's MSBuildCrackerResolver passes (`/p:FABLE_COMPILER=True`). On .NET the properties keep their defaults, so xunit.v3 still generates the test runner entry point. Verified `dotnet test -c Release` (Rust 2449 passing) and the Fable transpile (Rust/Python/Beam) all succeed with no xunit sources leaking into the transpiled output. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/Beam/Fable.Tests.Beam.fsproj | 7 +++++++ tests/Php/Fable.Tests.Php.fsproj | 7 +++++++ tests/Python/Fable.Tests.Python.fsproj | 7 +++++++ tests/Rust/Fable.Tests.Rust.fsproj | 8 ++++++++ tests/Rust/tests/src/lib.fs | 4 ++-- 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/Beam/Fable.Tests.Beam.fsproj b/tests/Beam/Fable.Tests.Beam.fsproj index e79318100e..aca94f1a8e 100644 --- a/tests/Beam/Fable.Tests.Beam.fsproj +++ b/tests/Beam/Fable.Tests.Beam.fsproj @@ -1,6 +1,13 @@ Exe + + false + false net10.0 Major false diff --git a/tests/Php/Fable.Tests.Php.fsproj b/tests/Php/Fable.Tests.Php.fsproj index ec49f9e604..19cad33349 100644 --- a/tests/Php/Fable.Tests.Php.fsproj +++ b/tests/Php/Fable.Tests.Php.fsproj @@ -1,6 +1,13 @@ Exe + + false + false net10.0 Major false diff --git a/tests/Python/Fable.Tests.Python.fsproj b/tests/Python/Fable.Tests.Python.fsproj index 79f2f1dc52..f17b993fb0 100644 --- a/tests/Python/Fable.Tests.Python.fsproj +++ b/tests/Python/Fable.Tests.Python.fsproj @@ -1,6 +1,13 @@ Exe + + false + false net10.0 Major false diff --git a/tests/Rust/Fable.Tests.Rust.fsproj b/tests/Rust/Fable.Tests.Rust.fsproj index d266d4d06c..867eb0d4b9 100644 --- a/tests/Rust/Fable.Tests.Rust.fsproj +++ b/tests/Rust/Fable.Tests.Rust.fsproj @@ -1,6 +1,14 @@ Exe + + false + false net10.0 Major false diff --git a/tests/Rust/tests/src/lib.fs b/tests/Rust/tests/src/lib.fs index f9b2027a20..ddaf02e64b 100644 --- a/tests/Rust/tests/src/lib.fs +++ b/tests/Rust/tests/src/lib.fs @@ -8,8 +8,8 @@ let _imports() = // [] #if FABLE_COMPILER -// On .NET, xunit.v3 generates the test runner entry point; only emit our own -// entry point when transpiling (Rust needs a `fn main`). +// Rust needs a `fn main`, so emit the entry point when transpiling. On .NET, +// xunit.v3 generates the test runner entry point instead (see the .fsproj). [] let main _args = 0 #endif From 782e103a0e3c2da526915a7a514d9804bcbdee78 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 28 May 2026 23:01:22 +0200 Subject: [PATCH 3/9] chore: run xunit serially in Python tests to avoid Windows flake Several MailboxProcessor/async tests assert from inside a fire-and-forget `Async.StartImmediate`, whose continuation can run after its test collection has been torn down. xunit.v3 reports such background-thread exceptions as catastrophic failures and exits non-zero (xunit v2 silently ignored them). With parallel test collections this surfaces on Windows as "Catastrophic failure: Assert.Equal() ... Expected 42, Actual 0". Add tests/Python/xunit.runner.json disabling test-collection parallelization so each collection fully drains before the next runs. This is the only xunit suite that runs on Windows in CI (Rust/Beam/Php .NET tests run on Linux only; the JS suite uses Expecto). Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/Python/Fable.Tests.Python.fsproj | 7 +++++++ tests/Python/xunit.runner.json | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 tests/Python/xunit.runner.json diff --git a/tests/Python/Fable.Tests.Python.fsproj b/tests/Python/Fable.Tests.Python.fsproj index f17b993fb0..e08e7b9d3a 100644 --- a/tests/Python/Fable.Tests.Python.fsproj +++ b/tests/Python/Fable.Tests.Python.fsproj @@ -29,6 +29,13 @@ + + + + diff --git a/tests/Python/xunit.runner.json b/tests/Python/xunit.runner.json new file mode 100644 index 0000000000..8641732346 --- /dev/null +++ b/tests/Python/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeTestCollections": false, + "maxParallelThreads": 1 +} From f348f64a374f39e0819fa8c1b21097071e831ac7 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 11 Jun 2026 16:13:41 +0200 Subject: [PATCH 4/9] chore(tests): extend serial xunit config to Beam/Php and tidy review feedback - Add xunit.runner.json to Beam and Php: their tests use the same fire-and-forget Async.StartImmediate pattern as Python, so they are exposed to the same xunit.v3 catastrophic background-exception failures. - Collapse the now-identical #if FABLE_COMPILER branches in Beam/Php/Python Main.fs and add the missing trailing newlines. - Compress the .fsproj comments explaining the cracking workaround. Co-Authored-By: Claude Fable 5 --- tests/Beam/Fable.Tests.Beam.fsproj | 14 +++++++++----- tests/Beam/Main.fs | 8 ++------ tests/Beam/xunit.runner.json | 5 +++++ tests/Php/Fable.Tests.Php.fsproj | 14 +++++++++----- tests/Php/Main.fs | 8 ++------ tests/Php/xunit.runner.json | 5 +++++ tests/Python/Fable.Tests.Python.fsproj | 15 ++++++--------- tests/Python/Main.fs | 8 ++------ tests/Rust/Fable.Tests.Rust.fsproj | 10 ++++------ 9 files changed, 44 insertions(+), 43 deletions(-) create mode 100644 tests/Beam/xunit.runner.json create mode 100644 tests/Php/xunit.runner.json diff --git a/tests/Beam/Fable.Tests.Beam.fsproj b/tests/Beam/Fable.Tests.Beam.fsproj index aca94f1a8e..1f3719664a 100644 --- a/tests/Beam/Fable.Tests.Beam.fsproj +++ b/tests/Beam/Fable.Tests.Beam.fsproj @@ -1,11 +1,9 @@ Exe - + false false net10.0 @@ -31,6 +29,12 @@ + + + + diff --git a/tests/Beam/Main.fs b/tests/Beam/Main.fs index 7cfe91c15e..34ec007612 100644 --- a/tests/Beam/Main.fs +++ b/tests/Beam/Main.fs @@ -1,8 +1,4 @@ -#if FABLE_COMPILER module Program +// No entry point: Fable does not need one here, and on .NET xunit.v3 +// generates the test runner entry point. () -#else -module Program -// On .NET, xunit.v3 generates the test runner entry point automatically. -() -#endif diff --git a/tests/Beam/xunit.runner.json b/tests/Beam/xunit.runner.json new file mode 100644 index 0000000000..8641732346 --- /dev/null +++ b/tests/Beam/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeTestCollections": false, + "maxParallelThreads": 1 +} diff --git a/tests/Php/Fable.Tests.Php.fsproj b/tests/Php/Fable.Tests.Php.fsproj index 19cad33349..ac82791e96 100644 --- a/tests/Php/Fable.Tests.Php.fsproj +++ b/tests/Php/Fable.Tests.Php.fsproj @@ -1,11 +1,9 @@ Exe - + false false net10.0 @@ -25,6 +23,12 @@ + + + + diff --git a/tests/Php/Main.fs b/tests/Php/Main.fs index d54c0fa690..34ec007612 100644 --- a/tests/Php/Main.fs +++ b/tests/Php/Main.fs @@ -1,8 +1,4 @@ -#if FABLE_COMPILER module Program +// No entry point: Fable does not need one here, and on .NET xunit.v3 +// generates the test runner entry point. () -#else -module Program -// On .NET, xunit.v3 generates the test runner entry point automatically. -() -#endif \ No newline at end of file diff --git a/tests/Php/xunit.runner.json b/tests/Php/xunit.runner.json new file mode 100644 index 0000000000..8641732346 --- /dev/null +++ b/tests/Php/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeTestCollections": false, + "maxParallelThreads": 1 +} diff --git a/tests/Python/Fable.Tests.Python.fsproj b/tests/Python/Fable.Tests.Python.fsproj index e08e7b9d3a..a1c78472d9 100644 --- a/tests/Python/Fable.Tests.Python.fsproj +++ b/tests/Python/Fable.Tests.Python.fsproj @@ -1,11 +1,9 @@ Exe - + false false net10.0 @@ -30,10 +28,9 @@ - + diff --git a/tests/Python/Main.fs b/tests/Python/Main.fs index d54c0fa690..34ec007612 100644 --- a/tests/Python/Main.fs +++ b/tests/Python/Main.fs @@ -1,8 +1,4 @@ -#if FABLE_COMPILER module Program +// No entry point: Fable does not need one here, and on .NET xunit.v3 +// generates the test runner entry point. () -#else -module Program -// On .NET, xunit.v3 generates the test runner entry point automatically. -() -#endif \ No newline at end of file diff --git a/tests/Rust/Fable.Tests.Rust.fsproj b/tests/Rust/Fable.Tests.Rust.fsproj index 867eb0d4b9..ce3e5130a6 100644 --- a/tests/Rust/Fable.Tests.Rust.fsproj +++ b/tests/Rust/Fable.Tests.Rust.fsproj @@ -1,12 +1,10 @@ Exe - + false false net10.0 From adcdaf310c3ff511a714f2d1da7be8f9c962e2b8 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 11 Oct 2026 19:03:36 +0200 Subject: [PATCH 5/9] chore(tests): upgrade xunit v3 to 4.0.2 --- tests/Beam/Fable.Tests.Beam.fsproj | 10 ++++++---- tests/Php/Fable.Tests.Php.fsproj | 10 ++++++---- tests/Python/Fable.Tests.Python.fsproj | 10 ++++++---- tests/Rust/Fable.Tests.Rust.fsproj | 12 +++++++----- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/tests/Beam/Fable.Tests.Beam.fsproj b/tests/Beam/Fable.Tests.Beam.fsproj index 1f3719664a..a608052c22 100644 --- a/tests/Beam/Fable.Tests.Beam.fsproj +++ b/tests/Beam/Fable.Tests.Beam.fsproj @@ -2,10 +2,11 @@ Exe + xunit.v3's generated entry point, runner reporters, and result writers: .NET-only sources + that break transpilation. Disable them for the Fable build; the real .NET build keeps them. --> false false + false net10.0 Major false @@ -15,8 +16,9 @@ - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Php/Fable.Tests.Php.fsproj b/tests/Php/Fable.Tests.Php.fsproj index ac82791e96..60a57811db 100644 --- a/tests/Php/Fable.Tests.Php.fsproj +++ b/tests/Php/Fable.Tests.Php.fsproj @@ -2,10 +2,11 @@ Exe + xunit.v3's generated entry point, runner reporters, and result writers: .NET-only sources + that break transpilation. Disable them for the Fable build; the real .NET build keeps them. --> false false + false net10.0 Major false @@ -14,8 +15,9 @@ - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Python/Fable.Tests.Python.fsproj b/tests/Python/Fable.Tests.Python.fsproj index a1c78472d9..9bbd125734 100644 --- a/tests/Python/Fable.Tests.Python.fsproj +++ b/tests/Python/Fable.Tests.Python.fsproj @@ -2,10 +2,11 @@ Exe + xunit.v3's generated entry point, runner reporters, and result writers: .NET-only sources + that break transpilation. Disable them for the Fable build; the real .NET build keeps them. --> false false + false net10.0 Major false @@ -15,8 +16,9 @@ - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Rust/Fable.Tests.Rust.fsproj b/tests/Rust/Fable.Tests.Rust.fsproj index ce3e5130a6..20f4b8a24b 100644 --- a/tests/Rust/Fable.Tests.Rust.fsproj +++ b/tests/Rust/Fable.Tests.Rust.fsproj @@ -2,11 +2,12 @@ Exe + xunit.v3's generated entry point, runner reporters, and result writers after + tests/src/main.fs, breaking transpilation (the entry point must be the last declaration, + and those sources are .NET-only). Disable them for the Fable build; the real .NET build keeps them. --> false false + false net10.0 Major false @@ -15,8 +16,9 @@ - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all From c009e72ffe96646fc6cf95782fadf64eb20e437e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 11 Oct 2026 19:12:20 +0200 Subject: [PATCH 6/9] chore(tests): limit xunit serialization to Python --- tests/Beam/Fable.Tests.Beam.fsproj | 6 ------ tests/Beam/xunit.runner.json | 5 ----- tests/Php/Fable.Tests.Php.fsproj | 6 ------ tests/Php/xunit.runner.json | 5 ----- tests/Python/Fable.Tests.Python.fsproj | 6 +++--- 5 files changed, 3 insertions(+), 25 deletions(-) delete mode 100644 tests/Beam/xunit.runner.json delete mode 100644 tests/Php/xunit.runner.json diff --git a/tests/Beam/Fable.Tests.Beam.fsproj b/tests/Beam/Fable.Tests.Beam.fsproj index a608052c22..c714ed508b 100644 --- a/tests/Beam/Fable.Tests.Beam.fsproj +++ b/tests/Beam/Fable.Tests.Beam.fsproj @@ -31,12 +31,6 @@ - - - - diff --git a/tests/Beam/xunit.runner.json b/tests/Beam/xunit.runner.json deleted file mode 100644 index 8641732346..0000000000 --- a/tests/Beam/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} diff --git a/tests/Php/Fable.Tests.Php.fsproj b/tests/Php/Fable.Tests.Php.fsproj index 60a57811db..3a4c662693 100644 --- a/tests/Php/Fable.Tests.Php.fsproj +++ b/tests/Php/Fable.Tests.Php.fsproj @@ -25,12 +25,6 @@ - - - - diff --git a/tests/Php/xunit.runner.json b/tests/Php/xunit.runner.json deleted file mode 100644 index 8641732346..0000000000 --- a/tests/Php/xunit.runner.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} diff --git a/tests/Python/Fable.Tests.Python.fsproj b/tests/Python/Fable.Tests.Python.fsproj index 9bbd125734..6f6a8f112a 100644 --- a/tests/Python/Fable.Tests.Python.fsproj +++ b/tests/Python/Fable.Tests.Python.fsproj @@ -30,9 +30,9 @@ - + From e88ac2ddd82bf406275779998682f7c05578586e Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 11 Oct 2026 19:28:02 +0200 Subject: [PATCH 7/9] chore(tests): remove empty entry files and restore Beam CI workaround --- tests/Beam/Fable.Tests.Beam.fsproj | 9 +++++---- tests/Beam/Main.fs | 4 ---- tests/Beam/xunit.runner.json | 5 +++++ tests/Php/Fable.Tests.Php.fsproj | 5 +---- tests/Php/Main.fs | 4 ---- tests/Python/Fable.Tests.Python.fsproj | 8 ++------ tests/Rust/Fable.Tests.Rust.fsproj | 5 +---- 7 files changed, 14 insertions(+), 26 deletions(-) delete mode 100644 tests/Beam/Main.fs create mode 100644 tests/Beam/xunit.runner.json delete mode 100644 tests/Php/Main.fs diff --git a/tests/Beam/Fable.Tests.Beam.fsproj b/tests/Beam/Fable.Tests.Beam.fsproj index c714ed508b..f310f1716d 100644 --- a/tests/Beam/Fable.Tests.Beam.fsproj +++ b/tests/Beam/Fable.Tests.Beam.fsproj @@ -1,9 +1,7 @@ Exe - + false false false @@ -31,6 +29,10 @@ + + + + @@ -111,6 +113,5 @@ - diff --git a/tests/Beam/Main.fs b/tests/Beam/Main.fs deleted file mode 100644 index 34ec007612..0000000000 --- a/tests/Beam/Main.fs +++ /dev/null @@ -1,4 +0,0 @@ -module Program -// No entry point: Fable does not need one here, and on .NET xunit.v3 -// generates the test runner entry point. -() diff --git a/tests/Beam/xunit.runner.json b/tests/Beam/xunit.runner.json new file mode 100644 index 0000000000..8641732346 --- /dev/null +++ b/tests/Beam/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeTestCollections": false, + "maxParallelThreads": 1 +} diff --git a/tests/Php/Fable.Tests.Php.fsproj b/tests/Php/Fable.Tests.Php.fsproj index 3a4c662693..b670ce39b8 100644 --- a/tests/Php/Fable.Tests.Php.fsproj +++ b/tests/Php/Fable.Tests.Php.fsproj @@ -1,9 +1,7 @@ Exe - + false false false @@ -38,7 +36,6 @@ - diff --git a/tests/Php/Main.fs b/tests/Php/Main.fs deleted file mode 100644 index 34ec007612..0000000000 --- a/tests/Php/Main.fs +++ /dev/null @@ -1,4 +0,0 @@ -module Program -// No entry point: Fable does not need one here, and on .NET xunit.v3 -// generates the test runner entry point. -() diff --git a/tests/Python/Fable.Tests.Python.fsproj b/tests/Python/Fable.Tests.Python.fsproj index 6f6a8f112a..52232d33ca 100644 --- a/tests/Python/Fable.Tests.Python.fsproj +++ b/tests/Python/Fable.Tests.Python.fsproj @@ -1,9 +1,7 @@ Exe - + false false false @@ -30,9 +28,7 @@ - + diff --git a/tests/Rust/Fable.Tests.Rust.fsproj b/tests/Rust/Fable.Tests.Rust.fsproj index 20f4b8a24b..dfd8096cf8 100644 --- a/tests/Rust/Fable.Tests.Rust.fsproj +++ b/tests/Rust/Fable.Tests.Rust.fsproj @@ -1,10 +1,7 @@ Exe - + false false false From 7f43878d2c8cb41f85a73795af186e8287fcfb63 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Sun, 11 Oct 2026 19:40:16 +0200 Subject: [PATCH 8/9] chore(tests): remove empty Python entry file --- tests/Python/Fable.Tests.Python.fsproj | 1 - tests/Python/Main.fs | 4 ---- 2 files changed, 5 deletions(-) delete mode 100644 tests/Python/Main.fs diff --git a/tests/Python/Fable.Tests.Python.fsproj b/tests/Python/Fable.Tests.Python.fsproj index 52232d33ca..92bfcf5106 100644 --- a/tests/Python/Fable.Tests.Python.fsproj +++ b/tests/Python/Fable.Tests.Python.fsproj @@ -97,6 +97,5 @@ - diff --git a/tests/Python/Main.fs b/tests/Python/Main.fs deleted file mode 100644 index 34ec007612..0000000000 --- a/tests/Python/Main.fs +++ /dev/null @@ -1,4 +0,0 @@ -module Program -// No entry point: Fable does not need one here, and on .NET xunit.v3 -// generates the test runner entry point. -() From a47b83e8d0ebc1817fbd1b08b2e4255f0122a524 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Mon, 12 Oct 2026 08:18:32 +0200 Subject: [PATCH 9/9] fix(rust): retain test harness without an application entry point --- src/Fable.Transforms/Rust/Fable2Rust.fs | 7 +++++-- tests/Rust/tests/src/lib.fs | 8 -------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index 6c0b7943cf..f12cb78d84 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -6480,12 +6480,16 @@ module Compiler = ModuleDepth = 0 } + let entryPointItems = file.Declarations |> getEntryPointItems com ctx + let topAttrs = [ if isLastFileInProject com then // adds "no_std" to crate if feature is enabled mkInnerAttr "cfg_attr" [ "feature = \"no_std\""; "no_std" ] - mkInnerAttr "cfg_attr" [ "feature = \"no_std\""; "no_main" ] + // Libraries retain Cargo's test-harness entry point. + if not (List.isEmpty entryPointItems) then + mkInnerAttr "cfg_attr" [ "feature = \"no_std\""; "no_main" ] // TODO: make some of those conditional on compiler options mkInnerAttr "allow" [ "dead_code" ] @@ -6507,7 +6511,6 @@ module Compiler = // mkInnerAttr "feature" ["destructuring_assignment"] ] - let entryPointItems = file.Declarations |> getEntryPointItems com ctx let importItems = com.GetAllImports(ctx) |> transformImports com ctx let declItems = file.Declarations |> transformDeclarations com ctx let modItems = getModuleItems com ctx // global module imports diff --git a/tests/Rust/tests/src/lib.fs b/tests/Rust/tests/src/lib.fs index ddaf02e64b..dd4a08ea7a 100644 --- a/tests/Rust/tests/src/lib.fs +++ b/tests/Rust/tests/src/lib.fs @@ -5,11 +5,3 @@ open Fable.Core.Rust let _imports() = importAll "./ExtInteropTests.rs" () - -// [] -#if FABLE_COMPILER -// Rust needs a `fn main`, so emit the entry point when transpiling. On .NET, -// xunit.v3 generates the test runner entry point instead (see the .fsproj). -[] -let main _args = 0 -#endif