krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

Packages/MoonriseKit/Tests/SkyRendererTests/SkyRenderTests.swift

79 lines · 3410 bytes

6 symbols in this file
 1import Foundation
 2import SkyEngine
 3import SwiftUI
 4import Testing
 5@testable import SkyRenderer
 6
 7let chicago = Observer(latitude: 41.8781, longitude: -87.6298)
 8let evening = Date(timeIntervalSince1970: 1_792_202_400) // 2026-10-17T02:00:00Z, 21:00 CDT
 9
10@Test func catalogLoads() {
11    #expect(Catalog.stars.count > 9_000)
12    #expect(Catalog.stars.contains { $0.hr == 2491 && $0.magnitude < -1 }) // Sirius
13}
14
15@Test func sceneHasSaturnUpAndMarsDown() throws {
16    let scene = try SkyScene(date: evening, observer: chicago)
17    let saturn = try #require(scene.bodies.first { $0.body == .saturn })
18    let mars = try #require(scene.bodies.first { $0.body == .mars })
19    #expect(saturn.position.altitude > 30)
20    #expect(mars.position.altitude < 0)
21}
22
23/// Renders the sky to a PNG and reports timings. Set SKY_RENDER_OUT to keep the image.
24@MainActor
25@Test func renderStaticSky() throws {
26    let clock = ContinuousClock()
27    var scene: SkyScene!
28    let build = try clock.measure { scene = try SkyScene(date: evening, observer: chicago) }
29
30    let renderer = ImageRenderer(content: SkyView(scene: scene).frame(width: 393, height: 852))
31    renderer.scale = 3
32    var image: CGImage?
33    let render = clock.measure { image = renderer.cgImage }
34    let cg = try #require(image)
35
36    print("scene: \(scene.stars.count) stars, build \(build); render \(cg.width)x\(cg.height) in \(render)")
37
38    if let out = ProcessInfo.processInfo.environment["SKY_RENDER_OUT"] {
39        let url = URL(fileURLWithPath: out)
40        let dest = try #require(CGImageDestinationCreateWithURL(url as CFURL, "public.png" as CFString, 1, nil))
41        CGImageDestinationAddImage(dest, cg, nil)
42        #expect(CGImageDestinationFinalize(dest))
43    }
44}
45
46@Test func tapOnSaturnFindsSaturnAndNearbyStarsFindTheirConstellation() throws {
47    let scene = try SkyScene(date: evening, observer: chicago)
48    let p = Projection(centerAzimuth: 180, centerAltitude: 45, fieldOfView: 100, size: CGSize(width: 393, height: 852))
49    let saturn = try #require(scene.bodies.first { $0.body == .saturn })
50    let pt = try #require(p.point(saturn.position))
51    #expect(scene.object(at: CGPoint(x: pt.x + 5, y: pt.y), projection: p) == .body(.saturn))
52
53    // Altair (HR 7557) is in Aquila's figure.
54    let altair = try #require(scene.starsByHR[7557])
55    let a = try #require(p.point(altair))
56    #expect(scene.object(at: a, projection: p) == .constellation("Aql"))
57}
58
59@MainActor
60@Test func nightStyleRenders() throws {
61    let scene = try SkyScene(date: evening, observer: chicago)
62    let renderer = ImageRenderer(content: SkyView(scene: scene, style: .night).frame(width: 393, height: 852))
63    #expect(renderer.cgImage != nil)
64}
65
66@Test func skyCulturesUseCatalogueStars() {
67    let cultures = Catalog.skyCultures
68    #expect(cultures.count == 10)
69    let known = Set(Catalog.stars.map(\.hr))
70    for c in cultures {
71        #expect(!c.constellations.isEmpty, "\(c.id)")
72        #expect(c.licence.contains("BY-SA") || c.licence.contains("BY SA"), "\(c.id)")
73        for f in c.constellations {
74            #expect(f.lines.allSatisfy { $0.count > 1 && $0.allSatisfy(known.contains) }, "\(c.id) \(f.name)")
75        }
76    }
77    // The Norse Wolf's Mouth is the Hyades and includes Aldebaran (HR 1457).
78    #expect(cultures.first { $0.id == "norse" }?.constellations.contains { $0.name == "Wolf's Mouth" && $0.lines.joined().contains(1457) } == true)
79}