marsdate 1.1.7 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/LICENSE.txt +21 -0
- data/README.md +650 -0
- data/bin/marsdate +16 -14
- data/lib/marsdate/calendar.rb +128 -0
- data/lib/marsdate/timescale.rb +314 -0
- data/lib/marsdate/version.rb +3 -0
- data/lib/marsdate.rb +291 -241
- data/marsdate.gemspec +31 -0
- data/test/calendar_test.rb +142 -0
- data/test/cli_test.rb +57 -0
- data/test/test.rb +305 -313
- data/test/timescale_test.rb +124 -0
- metadata +49 -11
- data/test/spec/marsdate_spec.rb +0 -382
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Tests for MarsDateTime::Calendar (MCE y/m/sol ↔ MSD).
|
|
3
|
+
# Run: ruby test/calendar_test.rb
|
|
4
|
+
|
|
5
|
+
require 'minitest/autorun'
|
|
6
|
+
require_relative '../lib/marsdate'
|
|
7
|
+
require 'date'
|
|
8
|
+
|
|
9
|
+
class CalendarTest < Minitest::Test
|
|
10
|
+
Cal = MarsDateTime::Calendar
|
|
11
|
+
TS = MarsDateTime::TimeScale
|
|
12
|
+
|
|
13
|
+
def test_leap_rule_matches_library
|
|
14
|
+
1.upto(2000) do |y|
|
|
15
|
+
assert_equal MarsDateTime.leap?(y), Cal.leap?(y), "leap? year #{y}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_closed_form_leaps
|
|
20
|
+
brute = 0
|
|
21
|
+
1.upto(2000) do |y|
|
|
22
|
+
brute += 1 if Cal.leap?(y)
|
|
23
|
+
assert_equal brute, Cal.leaps_through(y), "leaps_through(#{y})"
|
|
24
|
+
end
|
|
25
|
+
assert_equal 0, Cal.leaps_before(1)
|
|
26
|
+
assert_equal 1, Cal.leaps_before(2)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_mean_year_1000
|
|
30
|
+
assert_equal 591, Cal.leaps_through(1000)
|
|
31
|
+
assert_equal 668_000 + 591, Cal.sols_before_year(1001)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_epoch_is_year1_sol1
|
|
35
|
+
assert_equal 0, Cal.sol_index(1, 1, 1)
|
|
36
|
+
assert_equal Cal::EPOCH_MSD, Cal.msd_midnight(1, 1, 1)
|
|
37
|
+
assert_equal [1, 1, 1], Cal.ymd(0)
|
|
38
|
+
assert_equal [1, 1, 1], Cal.ymd_from_msd(Cal::EPOCH_MSD)
|
|
39
|
+
assert_equal [1, 1, 1], Cal.ymd_from_msd(Cal::EPOCH_MSD + 0.999)
|
|
40
|
+
assert_equal [1, 1, 2], Cal.ymd_from_msd(Cal::EPOCH_MSD + 1)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_year1_is_leap_taurus_25
|
|
44
|
+
assert Cal.leap?(1)
|
|
45
|
+
assert_equal 669, Cal.sols_in_year(1)
|
|
46
|
+
assert_equal 25, Cal.sols_in_month(24, 1)
|
|
47
|
+
assert_equal 668, Cal.sol_index(1, 24, 25)
|
|
48
|
+
assert_equal [1, 24, 25], Cal.ymd(668)
|
|
49
|
+
assert_equal [2, 1, 1], Cal.ymd(669)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_year2_is_short_taurus_24
|
|
53
|
+
refute Cal.leap?(2)
|
|
54
|
+
assert_equal 668, Cal.sols_in_year(2)
|
|
55
|
+
assert_equal 24, Cal.sols_in_month(24, 2)
|
|
56
|
+
last = Cal.sol_index(2, 24, 24)
|
|
57
|
+
assert_equal [2, 24, 24], Cal.ymd(last)
|
|
58
|
+
assert_equal [3, 1, 1], Cal.ymd(last + 1)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_century_and_millennium
|
|
62
|
+
refute Cal.leap?(100)
|
|
63
|
+
assert Cal.leap?(10)
|
|
64
|
+
assert Cal.leap?(1000)
|
|
65
|
+
assert_equal 24, Cal.sols_in_month(24, 100)
|
|
66
|
+
assert_equal 25, Cal.sols_in_month(24, 1000)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_round_trip_samples
|
|
70
|
+
samples = [
|
|
71
|
+
[1, 1, 1], [1, 1, 2], [1, 12, 14], [1, 24, 25],
|
|
72
|
+
[2, 1, 1], [2, 24, 24], [10, 24, 25], [100, 24, 24],
|
|
73
|
+
[1000, 24, 25], [1054, 20, 20], [1063, 1, 1]
|
|
74
|
+
]
|
|
75
|
+
samples.each do |y, m, d|
|
|
76
|
+
n = Cal.sol_index(y, m, d)
|
|
77
|
+
assert_equal [y, m, d], Cal.ymd(n), "round-trip index #{y}/#{m}/#{d}"
|
|
78
|
+
msd = Cal.msd_midnight(y, m, d)
|
|
79
|
+
assert_equal [y, m, d], Cal.ymd_from_msd(msd), "round-trip MSD #{y}/#{m}/#{d}"
|
|
80
|
+
assert_equal [y, m, d], Cal.ymd_from_msd(msd + 0.5), "noon still #{y}/#{m}/#{d}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def test_round_trip_scan
|
|
85
|
+
[[1, 12], [98, 102], [998, 1002]].each do |lo, hi|
|
|
86
|
+
(Cal.sols_before_year(lo)...Cal.sols_before_year(hi + 1)).each do |n|
|
|
87
|
+
y, m, d = Cal.ymd(n)
|
|
88
|
+
assert_equal n, Cal.sol_index(y, m, d), "scan index #{n} → #{y}/#{m}/#{d}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def test_reject_invalid
|
|
94
|
+
assert_raises(ArgumentError) { Cal.sol_index(2, 24, 25) }
|
|
95
|
+
assert_raises(ArgumentError) { Cal.sol_index(1, 24, 26) }
|
|
96
|
+
assert_raises(ArgumentError) { Cal.sol_index(1, 1, 0) }
|
|
97
|
+
assert_raises(ArgumentError) { Cal.sol_index(1, 0, 1) }
|
|
98
|
+
assert_raises(ArgumentError) { Cal.sol_index(1, 25, 1) }
|
|
99
|
+
assert_raises(ArgumentError) { Cal.sol_index(0, 1, 1) }
|
|
100
|
+
assert_raises(ArgumentError) { Cal.ymd(-1) }
|
|
101
|
+
assert_raises(ArgumentError) { Cal.msd_at(1, 1, 1, 1.0) }
|
|
102
|
+
assert_raises(ArgumentError) { Cal.msd_at(1, 1, 1, -0.01) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_sol_fraction_bounds
|
|
106
|
+
assert_equal Cal::EPOCH_MSD.to_f, Cal.msd_at(1, 1, 1, 0.0)
|
|
107
|
+
mid = Cal.msd_at(1, 1, 1, 0.5)
|
|
108
|
+
assert_equal [1, 1, 1], Cal.ymd_from_msd(mid)
|
|
109
|
+
assert_in_delta 0.5, mid - Cal::EPOCH_MSD, 1e-12
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_weekday_year1
|
|
113
|
+
assert_equal 0, Cal.dow(1, 1, 1)
|
|
114
|
+
assert_equal 1, Cal.dow(1, 1, 2)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_modern_earth_instant
|
|
118
|
+
# 2000-01-06 00:00 UTC is a Mars24 worked example (MTC 23:59:39,
|
|
119
|
+
# still the previous sol). Civil date is a regression pin on
|
|
120
|
+
# EPOCH_MSD + the MCE leap rule, not an independent authority.
|
|
121
|
+
dt = DateTime.new(2000, 1, 6, 0, 0, 0)
|
|
122
|
+
msd = TS.msd(dt)
|
|
123
|
+
assert_equal [1063, 19, 21], Cal.ymd_from_msd(msd)
|
|
124
|
+
assert_equal msd.floor - Cal::EPOCH_MSD, Cal.sol_index(1063, 19, 21)
|
|
125
|
+
|
|
126
|
+
dt2 = DateTime.new(2026, 8, 13, 0, 6, 56)
|
|
127
|
+
assert_equal [1077, 23, 6], Cal.ymd_from_msd(TS.msd(dt2))
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def test_epoch_report_agrees
|
|
131
|
+
report = TS.epoch_report
|
|
132
|
+
assert_equal Cal::EPOCH_MSD, report[:epoch_msd]
|
|
133
|
+
eq = report[:ls0_msd]
|
|
134
|
+
assert_equal [1, 1, 1], Cal.ymd_from_msd(eq)
|
|
135
|
+
assert_equal Cal::EPOCH_MSD, eq.floor
|
|
136
|
+
# Caption only: TT read as UT. Does not move EPOCH_MSD.
|
|
137
|
+
cap = report[:epoch_earth_if_tt_were_ut]
|
|
138
|
+
assert_equal [1, 1, 23, 13, 40],
|
|
139
|
+
[cap.year, cap.month, cap.day, cap.hour, cap.min]
|
|
140
|
+
assert_in_delta 27, cap.sec, 1
|
|
141
|
+
end
|
|
142
|
+
end
|
data/test/cli_test.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# CLI regression tests.
|
|
3
|
+
# Run: ruby test/cli_test.rb
|
|
4
|
+
|
|
5
|
+
require 'minitest/autorun'
|
|
6
|
+
require 'open3'
|
|
7
|
+
require 'rbconfig'
|
|
8
|
+
require_relative '../lib/marsdate'
|
|
9
|
+
|
|
10
|
+
class CliTest < Minitest::Test
|
|
11
|
+
ROOT = File.expand_path('..', __dir__)
|
|
12
|
+
BIN = File.join(ROOT, 'bin', 'marsdate')
|
|
13
|
+
LIB = File.join(ROOT, 'lib')
|
|
14
|
+
|
|
15
|
+
def test_calendar_does_not_highlight_today_in_another_year
|
|
16
|
+
other_year = [MarsDateTime.today.year - 1, 1].max
|
|
17
|
+
other_year += 1 if other_year == MarsDateTime.today.year
|
|
18
|
+
path = "/tmp/m#{other_year}.html"
|
|
19
|
+
|
|
20
|
+
stdout, stderr, status = run_cli('calendar', other_year.to_s)
|
|
21
|
+
assert status.success?, stderr
|
|
22
|
+
assert_includes stdout, path
|
|
23
|
+
html = File.read(path)
|
|
24
|
+
refute_includes html, 'bgcolor=FFD0BF'
|
|
25
|
+
refute_includes html, 'bgcolor=FFB0A0'
|
|
26
|
+
ensure
|
|
27
|
+
File.delete(path) if path && File.exist?(path)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_invocation_error_exits_nonzero
|
|
31
|
+
_stdout, stderr, status = run_cli('m2e', 'nonsense')
|
|
32
|
+
|
|
33
|
+
refute status.success?
|
|
34
|
+
assert_includes stderr, 'Error in calling m2e:'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_wrong_argument_count_exits_nonzero
|
|
38
|
+
stdout, stderr, status = run_cli('one', 'two', 'three', 'four')
|
|
39
|
+
|
|
40
|
+
refute status.success?
|
|
41
|
+
assert_includes stderr, 'Wrong number of arguments'
|
|
42
|
+
assert_includes stdout, 'Usage:'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_valid_command_exits_zero
|
|
46
|
+
stdout, stderr, status = run_cli('version')
|
|
47
|
+
|
|
48
|
+
assert status.success?, stderr
|
|
49
|
+
assert_equal "#{MarsDateTime::VERSION}\n", stdout
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def run_cli(*args)
|
|
55
|
+
Open3.capture3(RbConfig.ruby, "-I#{LIB}", BIN, *args)
|
|
56
|
+
end
|
|
57
|
+
end
|