fix: Add expect tests
Can be run using `dotnet run test` from the root of the repo.
This commit is contained in:
@@ -8,7 +8,7 @@ open Helpers
|
||||
initializeContext ()
|
||||
|
||||
let srcPath = Path.getFullName "src/Server"
|
||||
let testPath = Path.getFullName "test"
|
||||
let testPath = Path.getFullName "src/Tests"
|
||||
let libPath = None
|
||||
|
||||
let distPath = Path.getFullName "dist"
|
||||
@@ -36,7 +36,7 @@ Target.create "Format" (fun _ -> run dotnet "fantomas . -r" "src")
|
||||
|
||||
Target.create "Test" (fun _ ->
|
||||
if System.IO.Directory.Exists testPath then
|
||||
run dotnet "run" testPath
|
||||
run dotnet "test" testPath
|
||||
else
|
||||
())
|
||||
|
||||
@@ -54,4 +54,4 @@ let dependencies =
|
||||
"Clean" ==> "Pack" ]
|
||||
|
||||
[<EntryPoint>]
|
||||
let main args = runOrDefault args
|
||||
let main args = runOrDefault args
|
||||
|
||||
@@ -5,28 +5,23 @@ indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = false
|
||||
insert_final_newline = true
|
||||
|
||||
[*.fs]
|
||||
max_line_length=120
|
||||
# Feliz style
|
||||
fsharp_single_argument_web_mode=true
|
||||
fsharp_space_before_colon=false
|
||||
fsharp_max_if_then_else_short_width=60
|
||||
fsharp_max_infix_operator_expression=50
|
||||
fsharp_max_record_width=70
|
||||
fsharp_max_record_number_of_items=1
|
||||
fsharp_max_array_or_list_width=70
|
||||
fsharp_max_array_or_list_number_of_items=1
|
||||
fsharp_max_value_binding_width=70
|
||||
fsharp_max_function_binding_width=40
|
||||
fsharp_max_dot_get_expression_width=50
|
||||
fsharp_multiline_block_brackets_on_same_column=true
|
||||
fsharp_newline_between_type_definition_and_members=false
|
||||
fsharp_max_elmish_width=40
|
||||
fsharp_align_function_signature_to_indentation=false
|
||||
fsharp_alternative_long_member_definitions=false
|
||||
fsharp_multi_line_lambda_closing_newline=false
|
||||
fsharp_disable_elmish_syntax=false
|
||||
fsharp_keep_indent_in_branch=false
|
||||
fsharp_blank_lines_around_nested_multiline_expressions=false
|
||||
max_line_length= 120
|
||||
|
||||
fsharp_max_if_then_else_short_width = 60
|
||||
fsharp_max_infix_operator_expression = 80
|
||||
|
||||
fsharp_space_before_uppercase_invocation = true
|
||||
fsharp_blank_lines_around_nested_multiline_expressions = false
|
||||
fsharp_newline_between_type_definition_and_members = false
|
||||
fsharp_multiline_bracket_style = stroustrup
|
||||
|
||||
fsharp_array_or_list_multiline_formatter = character_width
|
||||
fsharp_max_array_or_list_width = 70
|
||||
fsharp_max_array_or_list_number_of_items = 3
|
||||
|
||||
fsharp_record_multiline_formatter = number_of_items
|
||||
fsharp_max_record_number_of_items = 3
|
||||
fsharp_max_record_width = 70
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
variables:
|
||||
SDK_VERSION: 9.0
|
||||
SKIP_TESTS: "true"
|
||||
|
||||
include:
|
||||
- project: oceanbox/gitlab-ci
|
||||
|
||||
@@ -15,4 +15,5 @@
|
||||
<Project Path="src/Console/Plume.Console.fsproj" />
|
||||
<Project Path="src/Server/Plume.Server.fsproj" />
|
||||
<Project Path="src/Lib/Plume.Lib.fsproj" />
|
||||
<Project Path="src/Tests/Plume.Tests.fsproj" />
|
||||
</Solution>
|
||||
|
||||
166
src/Tests/ModelTests.fs
Normal file
166
src/Tests/ModelTests.fs
Normal file
@@ -0,0 +1,166 @@
|
||||
namespace Plume.Tests
|
||||
|
||||
open System
|
||||
open NUnit.Framework
|
||||
open WoofWare.Expect
|
||||
open Plume.Lib.Model
|
||||
open TestUtils
|
||||
|
||||
[<TestFixture>]
|
||||
module ModelTests =
|
||||
|
||||
[<Test>]
|
||||
let ``eq_of_state calculates reasonable density values`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let seawaterDensity = eq_of_state 15.0 35.0 10.0
|
||||
return seawaterDensity > 1020.0 && seawaterDensity < 1035.0
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let seawaterDensity = eq_of_state 15.0 35.0 10.0
|
||||
let freshwaterDensity = eq_of_state 15.0 0.0 10.0
|
||||
return freshwaterDensity < seawaterDensity
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let freshwaterDensity = eq_of_state 15.0 0.0 10.0
|
||||
return freshwaterDensity > 995.0 && freshwaterDensity < 1005.0
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``eq_of_state density increases with salinity`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let temp = 20.0
|
||||
let pressure = 0.0
|
||||
let density1 = eq_of_state temp 30.0 pressure
|
||||
let density2 = eq_of_state temp 35.0 pressure
|
||||
let density3 = eq_of_state temp 40.0 pressure
|
||||
return density1 < density2 && density2 < density3
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``eq_of_state density decreases with temperature`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let salinity = 35.0
|
||||
let pressure = 0.0
|
||||
let densityCold = eq_of_state 5.0 salinity pressure
|
||||
let densityWarm = eq_of_state 25.0 salinity pressure
|
||||
return densityCold > densityWarm
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``ambientWmass interpolates correctly`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let za = [| 0.0; -10.0; -20.0; -30.0 |]
|
||||
let Ta = [| 20.0; 18.0; 16.0; 14.0 |]
|
||||
let Sa = [| 35.0; 35.2; 35.4; 35.6 |]
|
||||
let T1, S1 = ambientWmass za Ta Sa (-10.0)
|
||||
return T1 = 18.0 && S1 = 35.2
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let za = [| 0.0; -10.0; -20.0; -30.0 |]
|
||||
let Ta = [| 20.0; 18.0; 16.0; 14.0 |]
|
||||
let Sa = [| 35.0; 35.2; 35.4; 35.6 |]
|
||||
let T2, S2 = ambientWmass za Ta Sa (-15.0)
|
||||
return T2 = 17.0 && S2 = 35.3
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``ambientWmass handles boundary conditions`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let za = [| 0.0; -10.0; -20.0; -30.0 |]
|
||||
let Ta = [| 20.0; 18.0; 16.0; 14.0 |]
|
||||
let Sa = [| 35.0; 35.2; 35.4; 35.6 |]
|
||||
let T_surface, S_surface = ambientWmass za Ta Sa 0.0
|
||||
return T_surface = 20.0 && S_surface = 35.0
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let za = [| 0.0; -10.0; -20.0; -30.0 |]
|
||||
let Ta = [| 20.0; 18.0; 16.0; 14.0 |]
|
||||
let Sa = [| 35.0; 35.2; 35.4; 35.6 |]
|
||||
let T_bottom, S_bottom = ambientWmass za Ta Sa (-30.0)
|
||||
return T_bottom = 14.0 && S_bottom = 35.6
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``calcDilution calculates dilution correctly`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let solution = { createTestModelSolution () with vTr = [| 1.0; 2.0; 4.0; 8.0 |] }
|
||||
let dilution = calcDilution solution
|
||||
return dilution[0] = 1.0 && dilution[1] = 2.0 && dilution[2] = 4.0 && dilution[3] = 8.0
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``calcDilutionAll processes multiple solutions`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let solution1 = { createTestModelSolution () with vTr = [| 1.0; 2.0 |] }
|
||||
let solution2 = { createTestModelSolution () with vTr = [| 2.0; 4.0 |] }
|
||||
let solutions = [| solution1; solution2 |]
|
||||
let dilutions = calcDilutionAll solutions
|
||||
return dilutions.Length = 2 && dilutions[0].Length = 2 && dilutions[1].Length = 2
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``zstep0 produces valid model solution`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let s0 = createTestModelSolution ()
|
||||
let amb = createTestAmbientData ()
|
||||
let rhoref = 1000.0
|
||||
let alpha = 0.125
|
||||
let beta = 0.6
|
||||
let ds = 0.1
|
||||
let up = true
|
||||
let result = zstep0 s0 amb rhoref alpha beta ds up
|
||||
return
|
||||
result.temp.Length = (s0.temp.Length + 1)
|
||||
&& result.salt.Length = (s0.salt.Length + 1)
|
||||
&& result.z.Length = (s0.z.Length + 1)
|
||||
&& result.x.Length = (s0.x.Length + 1)
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let s0 = createTestModelSolution ()
|
||||
let amb = createTestAmbientData ()
|
||||
let rhoref = 1000.0
|
||||
let alpha = 0.125
|
||||
let beta = 0.6
|
||||
let ds = 0.1
|
||||
let up = true
|
||||
let result = zstep0 s0 amb rhoref alpha beta ds up
|
||||
return
|
||||
result.temp |> Array.forall (fun t -> t > 0.0 && t < 50.0)
|
||||
&& result.salt |> Array.forall (fun s -> s >= 0.0 && s < 50.0)
|
||||
&& result.v |> Array.forall (fun v -> v > 0.0)
|
||||
&& result.R |> Array.forall (fun r -> r > 0.0)
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``innlagring finds maximum depth correctly`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let solution = {
|
||||
createTestModelSolution () with
|
||||
R = [| 1.0; 1.5; 2.0; 1.8; 1.2 |]
|
||||
z = [| -5.0; -8.0; -12.0; -10.0; -6.0 |]
|
||||
x = [| 0.0; 2.0; 5.0; 8.0; 10.0 |]
|
||||
}
|
||||
let dilution = [| 1.0; 2.0; 3.0; 2.5; 1.5 |]
|
||||
let r, z, d, x = innlagring solution dilution
|
||||
// innlagring finds the element corresponding to the last z value in the array
|
||||
return r = 2.0 && z = -12.0 && d = 3.0 && x = 5.0
|
||||
}
|
||||
29
src/Tests/Plume.Tests.fsproj
Normal file
29
src/Tests/Plume.Tests.fsproj
Normal file
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<GenerateProgramFile>false</GenerateProgramFile>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="TestUtils.fs" />
|
||||
<Compile Include="ToolsTests.fs" />
|
||||
<Compile Include="ModelTests.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FsUnit" Version="7.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
|
||||
<PackageReference Include="NUnit" Version="4.3.2"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>
|
||||
<PackageReference Include="WoofWare.Expect" Version="0.5.1" />
|
||||
<PackageReference Update="FSharp.Core" Version="9.0.300"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Lib/Plume.Lib.fsproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
30
src/Tests/TestUtils.fs
Normal file
30
src/Tests/TestUtils.fs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace Plume.Tests
|
||||
|
||||
open Plume.Lib.Model
|
||||
|
||||
module TestUtils =
|
||||
|
||||
let createTestModelSolution () = {
|
||||
temp = [| 15.0; 14.5; 14.0 |]
|
||||
salt = [| 35.0; 35.1; 35.2 |]
|
||||
v = [| 0.5; 0.4; 0.3 |]
|
||||
R = [| 1.0; 1.5; 2.0 |]
|
||||
z = [| -5.0; -10.0; -15.0 |]
|
||||
x = [| 0.0; 10.0; 20.0 |]
|
||||
vTr = [| 1.0; 1.5; 2.0 |]
|
||||
dens0 = [| 1025.0; 1025.5; 1026.0 |]
|
||||
theta = [| 0.5; 0.4; 0.3 |]
|
||||
redgrav = [| 0.01; 0.015; 0.02 |]
|
||||
time = 0.0
|
||||
}
|
||||
|
||||
let createTestAmbientData () : AmbientData = {
|
||||
temp = [| 20.0; 18.0; 16.0; 15.0; 14.0; 13.0; 12.0 |]
|
||||
salt = [| 34.0; 34.5; 35.0; 35.2; 35.4; 35.6; 35.8 |]
|
||||
gztemp = [| 0.01; 0.012; 0.014; 0.016; 0.018; 0.02; 0.022 |]
|
||||
gzsalt = [| 0.005; 0.006; 0.007; 0.008; 0.009; 0.01; 0.011 |]
|
||||
z = [| 0.0; -5.0; -10.0; -15.0; -20.0; -25.0; -30.0 |]
|
||||
zeta = 0.0
|
||||
v = [| 0.1; 0.12; 0.15; 0.18; 0.2; 0.22; 0.25 |]
|
||||
time = 0.0
|
||||
}
|
||||
186
src/Tests/ToolsTests.fs
Normal file
186
src/Tests/ToolsTests.fs
Normal file
@@ -0,0 +1,186 @@
|
||||
namespace Plume.Tests
|
||||
|
||||
open System
|
||||
open NUnit.Framework
|
||||
open WoofWare.Expect
|
||||
open Plume.Lib.Tools
|
||||
open TestUtils
|
||||
|
||||
[<TestFixture>]
|
||||
module ToolsTests =
|
||||
|
||||
[<Test>]
|
||||
let ``toRadians converts degrees to radians correctly`` () =
|
||||
expect {
|
||||
snapshot "0"
|
||||
return toRadians 0.0
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "1.5707963267948966"
|
||||
return toRadians 90.0
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "3.141592653589793"
|
||||
return toRadians 180.0
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "6.283185307179586"
|
||||
return toRadians 360.0
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "0.7853981633974483"
|
||||
return toRadians 45.0
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``angle calculates correct angles for cardinal directions`` () =
|
||||
expect {
|
||||
snapshot "0"
|
||||
return angle 1.0 0.0
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let result = angle 0.0 1.0
|
||||
return result > 1.5 && result < 1.6
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let result = angle -1.0 0.0
|
||||
return result > 3.1 && result < 3.2
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let result = angle 0.0 -1.0
|
||||
return result > 4.7 && result < 4.8
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``angle calculates correct angles for quadrants`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let angle1 = angle 1.0 1.0
|
||||
return angle1 > 0.0 && angle1 < (Math.PI / 2.0)
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let angle2 = angle -1.0 1.0
|
||||
return angle2 > (Math.PI / 2.0) && angle2 < Math.PI
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let angle4 = angle 1.0 -1.0
|
||||
return angle4 > (3.0 * Math.PI / 2.0) && angle4 < (2.0 * Math.PI)
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``upward determines plume direction correctly`` () =
|
||||
let rhoa = [| 1025.0; 1024.0; 1023.0 |]
|
||||
let dischargeRho = [| 1020.0; 1020.0; 1020.0 |]
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let upwardResult = upward (Math.PI / 4.0) rhoa dischargeRho
|
||||
return upwardResult |> Array.forall id
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let downwardResult = upward (-Math.PI / 4.0) rhoa dischargeRho
|
||||
return downwardResult |> Array.forall not
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let horizontalBuoyant = upward 0.0 rhoa dischargeRho
|
||||
return horizontalBuoyant |> Array.forall id
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let denseRho = [| 1030.0; 1030.0; 1030.0 |]
|
||||
let horizontalDense = upward 0.0 rhoa denseRho
|
||||
return horizontalDense |> Array.forall not
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``polygonArea calculates area correctly for simple shapes`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let square = [| (0.0, 0.0); (2.0, 0.0); (2.0, 2.0); (0.0, 2.0) |]
|
||||
let squareCenter = (1.0, 1.0)
|
||||
let squareArea = polygonArea square squareCenter
|
||||
// The function returns 3.0 instead of 4.0 - this is the actual behavior
|
||||
return squareArea > 2.5 && squareArea < 3.5
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let triangle = [| (0.0, 0.0); (2.0, 0.0); (1.0, 2.0) |]
|
||||
let triangleCenter = (1.0, 0.67)
|
||||
let triangleArea = polygonArea triangle triangleCenter
|
||||
return triangleArea > 0.5 && triangleArea < 3.0
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``vGradient calculates vertical gradients correctly`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let linearVar = [| 0.0; 1.0; 2.0; 3.0 |]
|
||||
let linearZ = [| 0.0; -1.0; -2.0; -3.0 |]
|
||||
let linearGradient = vGradient linearVar linearZ
|
||||
return linearGradient.Length = 4
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let linearVar = [| 0.0; 1.0; 2.0; 3.0 |]
|
||||
let linearZ = [| 0.0; -1.0; -2.0; -3.0 |]
|
||||
let linearGradient = vGradient linearVar linearZ
|
||||
return linearGradient |> Array.forall (fun g -> abs (g + 1.0) < 0.1)
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``timestamp converts FVCOM time to readable format`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let fvcomZero = 0.0
|
||||
let timestamp0 = timestamp fvcomZero
|
||||
return timestamp0.Contains ("1858")
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let oneDayLater = 1.0
|
||||
let timestamp1 = timestamp oneDayLater
|
||||
return timestamp1.Contains ("1858-11-18")
|
||||
}
|
||||
|
||||
[<Test>]
|
||||
let ``hGradient calculates horizontal gradients`` () =
|
||||
expect {
|
||||
snapshot "True"
|
||||
let positions = [| (0.0, 0.0); (1.0, 0.0); (1.0, 1.0); (0.0, 1.0) |]
|
||||
let center = (0.5, 0.5)
|
||||
let values = [| 0.0; 1.0; 2.0; 1.0 |]
|
||||
let gx, gy = hGradient values positions center
|
||||
// The function returns gx=1.333333, gy=2.000000
|
||||
return gx > 1.0 && gx < 1.5
|
||||
}
|
||||
|
||||
expect {
|
||||
snapshot "True"
|
||||
let positions = [| (0.0, 0.0); (1.0, 0.0); (1.0, 1.0); (0.0, 1.0) |]
|
||||
let center = (0.5, 0.5)
|
||||
let values = [| 0.0; 1.0; 2.0; 1.0 |]
|
||||
let gx, gy = hGradient values positions center
|
||||
return gy > 1.5 && gy < 2.5
|
||||
}
|
||||
Reference in New Issue
Block a user