Skip to content

Commit 0078d60

Browse files
authored
Merge pull request #2994 from zonuexe/float-constant-drift
Remove stale Float constants and add a constant drift test
2 parents 5e0d76a + 1d46eba commit 0078d60

3 files changed

Lines changed: 131 additions & 24 deletions

File tree

core/float.rbs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,8 +1222,6 @@ Float::EPSILON: Float
12221222
#
12231223
Float::INFINITY: Float
12241224

1225-
Float::Infinity: Float
1226-
12271225
# <!-- rdoc-file=numeric.c -->
12281226
# The number of base digits for the `double` data type.
12291227
#
@@ -1292,25 +1290,3 @@ Float::NAN: Float
12921290
# decimal.
12931291
#
12941292
Float::RADIX: Integer
1295-
1296-
# Deprecated, do not use.
1297-
#
1298-
# Represents the rounding mode for floating point addition at the start time.
1299-
#
1300-
# Usually defaults to 1, rounding to the nearest number.
1301-
#
1302-
# Other modes include:
1303-
#
1304-
# -1
1305-
# : Indeterminable
1306-
# 0
1307-
# : Rounding towards zero
1308-
# 1
1309-
# : Rounding to the nearest number
1310-
# 2
1311-
# : Rounding towards positive infinity
1312-
# 3
1313-
# : Rounding towards negative infinity
1314-
#
1315-
#
1316-
Float::ROUNDS: Integer

test/stdlib/Float_test.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,59 @@
11
require_relative "test_helper"
22

3+
class FloatSingletonTest < Test::Unit::TestCase
4+
include TestHelper
5+
6+
testing 'singleton(::Float)'
7+
8+
def test_DIG
9+
assert_const_type 'Integer', 'Float::DIG'
10+
end
11+
12+
def test_EPSILON
13+
assert_const_type 'Float', 'Float::EPSILON'
14+
end
15+
16+
def test_INFINITY
17+
assert_const_type 'Float', 'Float::INFINITY'
18+
end
19+
20+
def test_MANT_DIG
21+
assert_const_type 'Integer', 'Float::MANT_DIG'
22+
end
23+
24+
def test_MAX
25+
assert_const_type 'Float', 'Float::MAX'
26+
end
27+
28+
def test_MAX_10_EXP
29+
assert_const_type 'Integer', 'Float::MAX_10_EXP'
30+
end
31+
32+
def test_MAX_EXP
33+
assert_const_type 'Integer', 'Float::MAX_EXP'
34+
end
35+
36+
def test_MIN
37+
assert_const_type 'Float', 'Float::MIN'
38+
end
39+
40+
def test_MIN_10_EXP
41+
assert_const_type 'Integer', 'Float::MIN_10_EXP'
42+
end
43+
44+
def test_MIN_EXP
45+
assert_const_type 'Integer', 'Float::MIN_EXP'
46+
end
47+
48+
def test_NAN
49+
assert_const_type 'Float', 'Float::NAN'
50+
end
51+
52+
def test_RADIX
53+
assert_const_type 'Integer', 'Float::RADIX'
54+
end
55+
end
56+
357
class FloatTest < StdlibTest
458
target Float
559

test/stdlib/constant_drift_test.rb

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
require_relative "test_helper"
2+
3+
# Guards against "constant drift" between the RBS core signatures and the actual
4+
# runtime, in both directions:
5+
#
6+
# * a constant declared in RBS but no longer defined by Ruby (e.g.
7+
# `Float::ROUNDS`, removed in Ruby 3.0), and
8+
# * a constant defined by Ruby but missing from RBS.
9+
#
10+
# Only platform- and build-invariant core classes are hard-gated here. Classes
11+
# whose constant set legitimately varies by OS or build options (`Process`,
12+
# `Socket`, `Errno`, `Signal`, `File::Constants`, `RbConfig`, `Etc`, ...) are
13+
# intentionally excluded: their RBS declarations cannot match any single
14+
# platform's runtime. `Object`/`BasicObject`/`Kernel` are excluded too, since
15+
# every top-level class shows up under `Object.constants`.
16+
class ConstantDriftTest < Test::Unit::TestCase
17+
# Platform/build-invariant core classes and modules whose declared constant
18+
# set must match the runtime exactly.
19+
HARD_GATE = [
20+
Float, Integer, Numeric, Rational, Complex,
21+
Math, Comparable,
22+
String, Symbol,
23+
Array, Hash, Range, Struct,
24+
NilClass, TrueClass, FalseClass
25+
].freeze
26+
27+
# Known, intentional exceptions keyed by "::Name" => [:CONST, ...]. Use this
28+
# for build- or platform-conditional constants (and `private_constant`s) that
29+
# are legitimately undeclared, so the gate stays green across CI platforms
30+
# without being weakened elsewhere.
31+
SKIP = {
32+
# Defined only when Ruby is built with GMP (USE_GMP): present on the Linux
33+
# CI build, absent on e.g. macOS.
34+
"::Integer" => [:GMP_VERSION]
35+
}.freeze
36+
37+
def env
38+
StdlibTest::DEFAULT_ENV
39+
end
40+
41+
# Constants declared directly under `type_name` in the loaded RBS environment
42+
# (plain constants plus nested classes/modules and their aliases), matching
43+
# what `Module#constants(false)` returns at runtime.
44+
def rbs_constants(type_name)
45+
prefix = "#{type_name}::"
46+
names = []
47+
[env.constant_decls, env.class_decls, env.class_alias_decls].each do |store|
48+
store.each_key do |tn|
49+
s = tn.to_s
50+
next unless s.start_with?(prefix)
51+
52+
rest = s.delete_prefix(prefix)
53+
names << rest.to_sym unless rest.include?("::")
54+
end
55+
end
56+
names.uniq.sort
57+
end
58+
59+
HARD_GATE.each do |klass|
60+
define_method(:"test_no_constant_drift_#{klass.name.gsub("::", "_")}") do
61+
type_name = "::#{klass.name}"
62+
skip = SKIP[type_name] || []
63+
runtime = (klass.constants(false) - skip).sort
64+
declared = (rbs_constants(type_name) - skip).sort
65+
66+
stale = declared - runtime
67+
missing = runtime - declared
68+
69+
assert_empty stale,
70+
"RBS declares #{type_name} constants that no longer exist at runtime: #{stale.inspect}. " \
71+
"Remove them from the signature (or add to ConstantDriftTest::SKIP if intentional)."
72+
assert_empty missing,
73+
"Runtime defines #{type_name} constants missing from RBS: #{missing.inspect}. " \
74+
"Add them to the signature (or add to ConstantDriftTest::SKIP if intentional)."
75+
end
76+
end
77+
end

0 commit comments

Comments
 (0)