91 lines
3.3 KiB
Elixir
91 lines
3.3 KiB
Elixir
defmodule MicrowavepropWeb.SkewtSvgTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias MicrowavepropWeb.SkewtSvg
|
|
|
|
@profile [
|
|
%{"pres" => 1013.0, "hght" => 100.0, "tmpc" => 25.0, "dwpc" => 18.0},
|
|
%{"pres" => 925.0, "hght" => 800.0, "tmpc" => 18.0, "dwpc" => 14.0},
|
|
%{"pres" => 850.0, "hght" => 1500.0, "tmpc" => 12.0, "dwpc" => 9.0},
|
|
%{"pres" => 700.0, "hght" => 3000.0, "tmpc" => 0.0, "dwpc" => -5.0},
|
|
%{"pres" => 500.0, "hght" => 5500.0, "tmpc" => -15.0, "dwpc" => -25.0},
|
|
%{"pres" => 300.0, "hght" => 9000.0, "tmpc" => -45.0, "dwpc" => -65.0}
|
|
]
|
|
|
|
describe "render/1" do
|
|
test "produces a valid <svg> root with the expected viewBox" do
|
|
svg = @profile |> SkewtSvg.render() |> IO.iodata_to_binary()
|
|
|
|
assert svg =~ ~r|<svg[^>]*viewBox="0 0 820 640"|
|
|
assert String.ends_with?(svg, "</svg>")
|
|
end
|
|
|
|
test "draws temperature and dewpoint polylines" do
|
|
svg = @profile |> SkewtSvg.render() |> IO.iodata_to_binary()
|
|
|
|
assert svg =~ ~s|class="temp-line"|
|
|
assert svg =~ ~s|class="dew-line"|
|
|
end
|
|
|
|
test "tolerates Rust-style atom-keyed levels" do
|
|
rust_profile = [
|
|
%{pres_mb: 1013.0, hght_m: 100.0, tmpc: 25.0, dwpc: 18.0},
|
|
%{pres_mb: 850.0, hght_m: 1500.0, tmpc: 12.0, dwpc: 9.0},
|
|
%{pres_mb: 500.0, hght_m: 5500.0, tmpc: -15.0, dwpc: -25.0}
|
|
]
|
|
|
|
svg = rust_profile |> SkewtSvg.render() |> IO.iodata_to_binary()
|
|
assert svg =~ ~s|class="temp-line"|
|
|
end
|
|
|
|
test "renders without trace polylines on an empty profile" do
|
|
svg = [] |> SkewtSvg.render() |> IO.iodata_to_binary()
|
|
|
|
# The style block always defines the trace classes; only the
|
|
# polyline elements should be missing for an empty profile.
|
|
assert svg =~ "<svg"
|
|
refute svg =~ ~s|<polyline class="temp-line"|
|
|
refute svg =~ ~s|<polyline class="dew-line"|
|
|
end
|
|
|
|
test "skips levels missing the dewpoint" do
|
|
partial = [
|
|
%{"pres" => 1000.0, "hght" => 100.0, "tmpc" => 20.0, "dwpc" => 10.0},
|
|
%{"pres" => 500.0, "hght" => 5000.0, "tmpc" => -10.0, "dwpc" => nil}
|
|
]
|
|
|
|
svg = partial |> SkewtSvg.render() |> IO.iodata_to_binary()
|
|
# Temperature line must still draw both points.
|
|
assert svg =~ ~s|class="temp-line"|
|
|
# Dewpoint line will only contain the bottom point and renders as
|
|
# nothing (single-point polyline) — that's fine, the test asserts
|
|
# the renderer didn't crash.
|
|
assert svg =~ "</svg>"
|
|
end
|
|
end
|
|
|
|
describe "coordinate transforms" do
|
|
test "pressure_y is monotonically decreasing as pressure decreases" do
|
|
y1000 = SkewtSvg.pressure_y(1000)
|
|
y500 = SkewtSvg.pressure_y(500)
|
|
y100 = SkewtSvg.pressure_y(100)
|
|
|
|
assert y1000 > y500
|
|
assert y500 > y100
|
|
end
|
|
|
|
test "temperature_x at the bottom edge: warmer = further right" do
|
|
cold = SkewtSvg.temperature_x(-20, SkewtSvg.pressure_y(1000))
|
|
warm = SkewtSvg.temperature_x(20, SkewtSvg.pressure_y(1000))
|
|
assert warm > cold
|
|
end
|
|
|
|
test "temperature_x is skewed: same temperature shifts right as pressure decreases" do
|
|
bottom = SkewtSvg.temperature_x(0, SkewtSvg.pressure_y(1000))
|
|
top = SkewtSvg.temperature_x(0, SkewtSvg.pressure_y(300))
|
|
# Skew-T isotherms lean lower-left → upper-right, so the same
|
|
# 0 °C plotted at 300 mb sits to the right of 0 °C at 1000 mb.
|
|
assert top > bottom
|
|
end
|
|
end
|
|
end
|