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/ObservingLogTests/ProgramsTests.swift

59d401dca1a327705632d1e2e20f00f807dee828
moonrise/Packages/MoonriseKit/Tests/ObservingLogTests/ProgramsTests.swift history · blame · raw

105 lines · 4521 bytes

6 symbols in this file
  1import Foundation
  2import Testing
  3@testable import ObservingLog
  4
  5@Test func designations() {
  6    #expect(CatalogResolver.designation("M31") == "M31")
  7    #expect(CatalogResolver.designation("M 31") == "M31")
  8    #expect(CatalogResolver.designation(" messier 31 ") == "M31")
  9    #expect(CatalogResolver.designation("M031") == "M31")
 10    #expect(CatalogResolver.designation("NGC 6205") == "NGC6205")
 11    #expect(CatalogResolver.designation("ic434") == "IC434")
 12    #expect(CatalogResolver.designation("Saturn") == nil)
 13    #expect(CatalogResolver.designation("M31 and M32") == nil)
 14}
 15
 16@Test func messierNamesResolve() {
 17    let r = CatalogResolver.messier
 18    for name in ["M31", "M 31", "Messier 31", "Andromeda Galaxy", "andromeda  galaxy"] {
 19        #expect(r.id(for: name) == "M31", "\(name)")
 20    }
 21    #expect(r.id(for: "Ptolemy’s Cluster") == "M7")
 22    #expect(r.id(for: "Winnecke 4") == "M40")
 23    #expect(r.id(for: "M102") == "M102")
 24    #expect(r.id(for: "Identity disputed") == nil)
 25    #expect(r.id(for: "Saturn") == nil)
 26}
 27
 28@Test func messierProgramHasAll110() throws {
 29    let p = ObservingProgram.messier
 30    #expect(p.objects.map(\.id) == (1...110).map { "M\($0)" })
 31    let m31 = try #require(p.objects.first { $0.id == "M31" })
 32    #expect(m31.aliases == ["Andromeda Galaxy"])
 33    #expect(m31.ra != nil && m31.dec != nil)
 34    // M102 has no position: OpenNGC lists it as a duplicate of M101 (see scripts/make-messier.py).
 35    let m102 = try #require(p.objects.first { $0.id == "M102" })
 36    #expect(m102.ra == nil && m102.dec == nil && m102.aliases.isEmpty)
 37    #expect(p.objects.filter { $0.ra == nil }.count == 1)
 38}
 39
 40@Test func programFromOrg() throws {
 41    let text = """
 42    #+TITLE: Autumn clusters
 43    Open clusters for a small refractor.
 44
 45    * Double Cluster
 46    :PROPERTIES:
 47    :CATALOG: NGC869 NGC884
 48    :RA: 34.75
 49    :DEC: 57.13
 50    :END:
 51    Two clusters in one field.
 52    ** Notes
 53    * M 45
 54    * Pleiades
 55    * Stock 2
 56    * Kemble's Cascade
 57    """
 58    let p = ObservingProgram(org: text, id: "autumn-clusters")
 59    #expect(p.title == "Autumn clusters")
 60    #expect(p.objects.map(\.id) == ["NGC869", "M45", "M45", "STOCK2", "kemble's cascade"])
 61    #expect(p.objects.map(\.name) == ["Double Cluster", "M 45", "Pleiades", "Stock 2", "Kemble's Cascade"])
 62    #expect(p.objects[0].ra == 34.75 && p.objects[0].dec == 57.13)
 63    #expect(p.objects[0].aliases == ["Double Cluster"])
 64    // Messier objects take their position from the bundled catalogue.
 65    #expect(p.objects[1].ra == ObservingProgram.messier.objects[44].ra)
 66    #expect(p.objects[3].ra == nil)
 67
 68    #expect(ObservingProgram(org: "* M13\n", id: "globulars").title == "globulars")
 69
 70    let r = CatalogResolver(programs: [.messier, p])
 71    #expect(r.id(for: "double cluster") == "NGC869")
 72    #expect(r.id(for: "Andromeda Galaxy") == "M31")
 73}
 74
 75@Test func programsFolder() throws {
 76    let dir = FileManager.default.temporaryDirectory.appending(path: "obs-\(UUID().uuidString)")
 77    defer { try? FileManager.default.removeItem(at: dir) }
 78    let log = ObservingLog(folder: dir)
 79    #expect(log.programs().map(\.id) == ["messier"])
 80    try FileManager.default.createDirectory(at: log.programsFolder, withIntermediateDirectories: true)
 81    try "#+TITLE: Globulars\n* M13\n* M92\n".write(to: log.programsFolder.appending(path: "globulars.org"), atomically: true, encoding: .utf8)
 82    try "notes".write(to: log.programsFolder.appending(path: "readme.txt"), atomically: true, encoding: .utf8)
 83    #expect(log.programs().map(\.title) == ["Messier", "Globulars"])
 84    // Program files are not log entries.
 85    #expect(log.all().isEmpty)
 86}
 87
 88@Test func progressCountsEachObjectOnce() throws {
 89    let resolver = CatalogResolver.messier
 90    let early = when.addingTimeInterval(-86_400)
 91    let log = [
 92        Observation(object: "Andromeda Galaxy", date: when, place: ""),
 93        Observation(object: "M 31", date: early, place: ""),
 94        Observation(object: "Dust lanes", date: when, place: "", catalog: ["M32"]),
 95        Observation(object: "Saturn", date: when, place: ""),
 96        Observation(object: "Kemble's Cascade", date: when, place: ""),
 97    ]
 98    let progress = ProgramProgress(program: .messier, log: log, resolver: resolver)
 99    #expect(progress.count == 2)
100    #expect(progress.first["M31"]?.date == early)
101    #expect(progress.first["M32"] != nil)
102
103    let mine = ObservingProgram(org: "* Kemble’s Cascade\n* M31\n", id: "mine")
104    #expect(ProgramProgress(program: mine, log: log, resolver: resolver).count == 2)
105}