LowestForecastTests.swift 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // LoopFollow
  2. // LowestForecastTests.swift
  3. import Foundation
  4. @testable import LoopFollow
  5. import Testing
  6. @Suite(.serialized)
  7. struct LowestForecastTests {
  8. private let start: TimeInterval = 1_000_000
  9. // MARK: - Combining
  10. @Test("#takes the per-point minimum across forecasts")
  11. func perPointMinimum() {
  12. let forecasts: [[Double]] = [
  13. [150, 140, 130, 120],
  14. [120, 130, 100, 160],
  15. [130, 110, 140, 90],
  16. ]
  17. let result = MainViewController.lowestForecast(forecasts: forecasts, start: start)
  18. #expect(result.map(\.sgv) == [120, 110, 100, 90])
  19. }
  20. @Test("#empty forecasts are ignored")
  21. func emptyForecastsIgnored() {
  22. let forecasts: [[Double]] = [
  23. [],
  24. [120, 110, 100],
  25. [],
  26. ]
  27. let result = MainViewController.lowestForecast(forecasts: forecasts, start: start)
  28. #expect(result.map(\.sgv) == [120, 110, 100])
  29. }
  30. @Test("#all empty yields no points")
  31. func allEmpty() {
  32. let result = MainViewController.lowestForecast(forecasts: [[], []], start: start)
  33. #expect(result.isEmpty)
  34. }
  35. @Test("#no forecasts yields no points")
  36. func noForecasts() {
  37. let result = MainViewController.lowestForecast(forecasts: [], start: start)
  38. #expect(result.isEmpty)
  39. }
  40. @Test("#uneven lengths use whichever forecasts still extend")
  41. func unevenLengths() {
  42. let forecasts: [[Double]] = [
  43. [100, 90], // shorter
  44. [110, 95, 80, 70], // longer
  45. ]
  46. let result = MainViewController.lowestForecast(forecasts: forecasts, start: start)
  47. // Points 0-1 use the min of both; points 2-3 use only the longer forecast.
  48. #expect(result.map(\.sgv) == [100, 90, 80, 70])
  49. }
  50. // MARK: - Capping
  51. @Test("#caps the number of points")
  52. func capsPoints() {
  53. let long = Array(repeating: 100.0, count: 30)
  54. let result = MainViewController.lowestForecast(forecasts: [long], start: start, cap: 12)
  55. #expect(result.count == 12)
  56. }
  57. @Test("#default cap is 12 points")
  58. func defaultCap() {
  59. let long = Array(repeating: 100.0, count: 30)
  60. let result = MainViewController.lowestForecast(forecasts: [long], start: start)
  61. #expect(result.count == MainViewController.alarmForecastPointCap)
  62. #expect(result.count == 12)
  63. }
  64. // MARK: - Timestamps & rounding
  65. @Test("#points are spaced 5 minutes from start")
  66. func timestampSpacing() {
  67. let forecasts: [[Double]] = [[100, 100, 100]]
  68. let result = MainViewController.lowestForecast(forecasts: forecasts, start: start)
  69. #expect(result.map { $0.date.timeIntervalSince1970 } == [start, start + 300, start + 600])
  70. }
  71. @Test("#values are rounded to the nearest integer")
  72. func rounding() {
  73. let forecasts: [[Double]] = [[100.4, 100.6, 99.5]]
  74. let result = MainViewController.lowestForecast(forecasts: forecasts, start: start)
  75. #expect(result.map(\.sgv) == [100, 101, 100])
  76. }
  77. }