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.
@@ -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