Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@
https://github.com/asc-community/AngouriMath/issues/542
-->
<IsPackable>false</IsPackable>

<!--
NU1608: FSharp.Compiler.Service 43.9.300 requires FSharp.Core (= 9.0.300), and what is
resolved is the 10.1 that the F# SDK references implicitly for every F# project. Sixteen
occurrences in the publish run for 2.3.0 and sixteen in the one for 2.4.0, so it is neither
new nor going away on its own: the constraint can only be met by pinning FSharp.Core two
majors back, against the SDK, for every project in the graph.

Suppressed because it was measured rather than because it was noisy. A kernel built on this
pair runs generic inference, records, unions, sequences, async, units of measure, object
expressions, inline constraints, active patterns, classes and string interpolation, and
reports a type error and a syntax error with the right positions — sixteen of sixteen, in
CompilerServiceWorksTest, which is what now carries the claim. A warning nobody reads is not
a check; a test that runs the compiler is.

If FSharp.Core or FSharp.Compiler.Service moves, that test is what should be believed, and
this comment is what should be rewritten.
https://github.com/asc-community/AngouriMath/issues/1100
-->
<NoWarn>$(NoWarn);NU1608</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
<Description>F#-based command line interface for symbolic algebra library AngouriMath</Description>

<PackageTags>$(PackageTags), cli, terminal</PackageTags>

<!--
The same NU1608 as AngouriMath.Terminal.Lib's, reaching here through the project reference,
and suppressed on the same measurement. Its comment is the one to read.
https://github.com/asc-community/AngouriMath/issues/1100
-->
<NoWarn>$(NoWarn);NU1608</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
84 changes: 84 additions & 0 deletions Sources/Tests/TerminalUnitTests/CompilerServiceWorksTest.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/// The Terminal bundles FSharp.Compiler.Service beside an FSharp.Core two majors above the one
/// it declares it needs, and NU1608 has said so on every publish since 2.3.0. This is what
/// settles whether that matters: the pair is asked to compile F#, and told to.
/// https://github.com/asc-community/AngouriMath/issues/1100
module AngouriMath.Terminal.CompilerServiceWorksTest

open Xunit
open AngouriMath.Terminal.Lib.FSharpInteractive
open AngouriMath.Terminal.Lib.PreRunCode
open AngouriMath.Terminal.Lib.Consts

/// A kernel with nothing loaded into it, so what is under test is the compiler rather than
/// AngouriMath's own bindings. The Terminal opens AggressiveOperators over the top, which
/// deliberately rebinds the comparison operators to build expressions — that is the tool's whole
/// point and it is not what this asks about.
let private bareKernel () =
match createKernel () with
| Result.Error _ ->
Assert.True(false, "the kernel did not load at all")
raise (System.Exception())
| Result.Ok kernel -> kernel

let private evaluates (code: string) (expected: string) =
match execute (bareKernel ()) code with
| ExecutionResult.PlainTextSuccess actual -> Assert.Equal(expected, actual)
| other ->
Assert.True(false, $"expected {expected}, and the kernel answered {other}")

/// Each of these is a language feature the compiler service has to implement rather than a
/// value the parser can fold, so a mismatched FSharp.Core would show here rather than in
/// arithmetic. Sixteen of sixteen pass on the shipped pair.
[<Theory>]
// Inference and generics.
[<InlineData("let twice f x = f (f x)\ntwice (fun n -> n * 2) 5", "20")>]
[<InlineData("let inline add a b = a + b\nadd 2 3", "5")>]
// Type definitions of every shape.
[<InlineData("type P = { X: int; Y: int }\nlet p = { X = 1; Y = 2 }\np.X + p.Y", "3")>]
[<InlineData("type T = A of int | B\nmatch A 7 with A n -> n | B -> 0", "7")>]
[<InlineData("type C(x: int) =\n member _.Doubled = x * 2\nC(21).Doubled", "42")>]
[<InlineData("[<Measure>] type m\nlet d = 5.0<m>\nfloat d", "5")>]
// Computation expressions and the library that backs them.
[<InlineData("async { return 1 + 1 } |> Async.RunSynchronously", "2")>]
[<InlineData("seq { 1..100 } |> Seq.filter (fun x -> x % 7 = 0) |> Seq.length", "14")>]
[<InlineData("[ for i in 1..10 -> i * i ] |> List.sum", "385")>]
// Pattern matching, including one the compiler generates code for.
[<InlineData("let (|Even|Odd|) n = if n % 2 = 0 then Even else Odd\nmatch 4 with Even -> \"e\" | Odd -> \"o\"", "e")>]
[<InlineData("[(1,2);(3,4)] |> List.map (fun (a,b) -> a + b)", "[3; 7]")>]
[<InlineData("let f x = if x > 0 then Some x else None\nOption.defaultValue 0 (f 5)", "5")>]
// Recursion, formatting, interpolation, and an object expression over an interface.
[<InlineData("let rec fib n = if n < 2 then n else fib (n-1) + fib (n-2)\nfib 20", "6765")>]
[<InlineData("sprintf \"%d %s %.2f\" 42 \"x\" 3.14159", "42 x 3.14")>]
[<InlineData("let n = 6\n$\"n is {n}\"", "n is 6")>]
[<InlineData("let c = { new System.IComparable<int> with member _.CompareTo o = 0 }\nc.CompareTo 1", "0")>]
let ``The compiler service compiles F#`` (code: string) (expected: string) =
evaluates code expected

/// And it still refuses what it should, with a position — a compiler that accepted everything
/// would pass the theory above and be useless.
[<Theory>]
[<InlineData("let x: int = \"not an int\"", "typecheck error")>]
[<InlineData("let =", "parse error")>]
let ``The compiler service reports errors`` (code: string) (expected: string) =
match execute (bareKernel ()) code with
| ExecutionResult.Error message ->
Assert.Contains(expected, message)
// A position, not just a complaint: the Terminal shows these to whoever typed them.
Assert.Contains("input.fsx (1,", message)
| other ->
Assert.True(false, $"expected an error and the kernel answered {other}")

/// The pairing the warning is about, doing the work the Terminal exists for.
[<Theory>]
[<InlineData("solutions \"x\" \"x2 - 5x + 6 = 0\"", "{ 2, 3 }")>]
[<InlineData("derivative \"x\" \"x3 + sin(x)\"", "3 * x ^ 2 + cos(x)")>]
[<InlineData("integral \"x\" \"x2\"", "x ^ 3 / 3 + C")>]
let ``AngouriMath works through the compiler service`` (code: string) (expected: string) =
let kernel = bareKernel ()
match enableAngouriMath kernel with
| ExecutionResult.Error reason -> Assert.True(false, $"AngouriMath did not load: {reason}")
| _ -> ()
match execute kernel code with
| ExecutionResult.LatexSuccess (_, actual) -> Assert.Equal(expected, actual)
| ExecutionResult.PlainTextSuccess actual -> Assert.Equal(expected, actual)
| other -> Assert.True(false, $"expected {expected}, and the kernel answered {other}")
1 change: 1 addition & 0 deletions Sources/Tests/TerminalUnitTests/TerminalUnitTests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<ItemGroup>
<Compile Include="Tests.fs" />
<Compile Include="CompilerServiceWorksTest.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

Expand Down
Loading