ractor-shim 0.0.1 → 0.1.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.
data/test/run_tests.rb ADDED
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ractor/shim'
4
+
5
+ BUILTIN_SKIPS = [
6
+ "send shareable and unshareable objects", # leaks Ractors on 3.3
7
+ "Ractor::IsolationError cases", # Ractor.make_shareable too permissive on 3.4
8
+ "Ractor.select from two ractors.", # Ractor.select with ports doesn't work on 3.4
9
+ "SystemExit from a Ractor is re-raised", # Ractor::ClosedError on 3.4 instead of Ractor::RemoteError
10
+ "SystemExit from a Thread inside a Ractor is re-raised", # Ractor::ClosedError on 3.4 instead of Ractor::RemoteError
11
+ "Access to global-variables are prohibited (read)", # different error message on 3.4
12
+ "Access to global-variables are prohibited (write)", # different error message on 3.4
13
+ "Ractor-local storage", # more permissive on 3.4
14
+ "Now NoMethodError is copyable", # fails on 3.4
15
+ "bind_call in Ractor [Bug #20934]", # SEGV on 3.4
16
+ "moved objects being corrupted if embeded (Hash)", # broken on 3.4
17
+ "moved objects being corrupted if embeded (MatchData)", # SEGV on 3.4
18
+ "moved objects being corrupted if embeded (Struct)", # broken on 3.4
19
+ "moved objects being corrupted if embeded (Object)", # broken on 3.4
20
+ "moved arrays can't be used", # broken on 3.4
21
+ "moved strings can't be used", # broken on 3.4
22
+ "moved hashes can't be used", # broken on 3.4
23
+ "move objects inside frozen containers", # broken on 3.4
24
+ "moved composite types move their non-shareable parts properly", # broken on 3.4
25
+ "Creating classes inside of Ractors", # OOM on 3.4
26
+ "Ractor#join raises RemoteError when the remote Ractor aborted with an exception", # needs join semantics for exceptions
27
+ "Only one Ractor can call Ractor#value", # need value semantics
28
+ "monitor port returns `:exited` when the monitering Ractor terminated.", # Ractor#monitor
29
+ "monitor port returns `:exited` even if the monitoring Ractor was terminated.", # Ractor#monitor
30
+ "monitor returns false if the monitoring Ractor was terminated.", # Ractor#monitor
31
+ "monitor port returns `:aborted` when the monitering Ractor is aborted.", # Ractor#monitor
32
+ "monitor port returns `:aborted` even if the monitoring Ractor was aborted.", # Ractor#monitor
33
+ "eval with outer locals in a Ractor raises SyntaxError", # can't check
34
+ ]
35
+
36
+ SHIM_SKIPS = [
37
+ "Ractor.allocate is not supported",
38
+ "Ractor::IsolationError cases",
39
+ "$DEBUG, $VERBOSE are Ractor local", # mutates global variables like $DEBUG and $VERBOSE
40
+ "SystemExit from a Thread inside a Ractor is re-raised", # internal error on TruffleRuby
41
+ "unshareable object are copied", # expected due to no copy
42
+ "To copy the object, now Marshal#dump is used", # expected due to no copy
43
+ "send shareable and unshareable objects", # expected due to no copy
44
+ "frozen Objects are shareable", # expected due to no copy
45
+ "touching moved object causes an error", # expected due to no move
46
+ "move example2: Array", # expected due to no move
47
+ "Access to global-variables are prohibited (read)", # can't check
48
+ "Access to global-variables are prohibited (write)", # can't check
49
+ "$stdin,out,err is Ractor local, but shared fds", # can't do
50
+ "given block Proc will be isolated, so can not access outer variables.", # can't check
51
+ "ivar in shareable-objects are not allowed to access from non-main Ractor", # not checked
52
+ "ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (get)", # not checked
53
+ "ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (set)", # not checked
54
+ "and instance variables of classes/modules are accessible if they refer shareable objects", # not checked
55
+ "cvar in shareable-objects are not allowed to access from non-main Ractor", # can't check
56
+ "also cached cvar in shareable-objects are not allowed to access from non-main Ractor", # can't check
57
+ "Getting non-shareable objects via constants by other Ractors is not allowed", # can't check
58
+ "Constant cache should care about non-shareable constants", # can't check
59
+ "Setting non-shareable objects into constants by other Ractors is not allowed", # can't check
60
+ "define_method is not allowed", # can't check
61
+ "ObjectSpace._id2ref can not handle unshareable objects with Ractors", # can't check
62
+ "Ractor.make_shareable(obj)", # expected due to no freeze
63
+ "Ractor.make_shareable(obj) doesn't freeze shareable objects", # expected due to no freeze
64
+ "Ractor.shareable?(recursive_objects)", # expected, due to all are shareable
65
+ "Ractor.make_shareable(recursive_objects)", # expected, due to all are shareable
66
+ "Ractor.make_shareable(obj, copy: true) makes copied shareable object.", # expected, due to all are shareable
67
+ "Can not trap with not isolated Proc on non-main ractor", # can't check
68
+ "Ractor-local storage with Thread inheritance of current Ractor", # hard
69
+ "Chilled strings are not shareable", # expected, due to all are shareable
70
+ "moved arrays can't be used", # expected due to no move
71
+ "moved strings can't be used", # expected due to no move
72
+ "moved hashes can't be used", # expected due to no move
73
+ "moved composite types move their non-shareable parts properly", # expected due to no move
74
+ "Only one Ractor can call Ractor#value", # could be implemented
75
+ "eval with outer locals in a Ractor raises SyntaxError", # can't check
76
+ ]
77
+
78
+ GLOBAL_SKIPS = [
79
+ "Ractor.make_shareable(a_proc) requires a shareable receiver", # because this harness uses Object as outer self
80
+ "threads in a ractor will killed", # leaks Ractors
81
+ "TracePoint with normal Proc should be Ractor local", # line numbers differ due to test harness
82
+ "Can yield back values while GC is sweeping [Bug #18117]", # too slow and leaks
83
+ "check experimental warning", # harness disables experimental warnings
84
+ "failed in autolaod in Ractor", # need more isolation
85
+ "fork after creating Ractor", # fork, leaks
86
+ "Ractors should be terminated after fork", # fork, hangs
87
+ "st_table, which can call malloc.", # leaks
88
+ "Creating classes inside of Ractors", # leaks
89
+ ]
90
+
91
+ skips = GLOBAL_SKIPS
92
+ skips += BUILTIN_SKIPS if Ractor.builtin? && RUBY_VERSION < "3.5"
93
+ skips += SHIM_SKIPS if Ractor.shim?
94
+ if Ractor.builtin? && RUBY_VERSION >= "3.5"
95
+ # fails on 3.5+
96
+ skips += [
97
+ "move object with generic ivar", # SEGV
98
+ "move object with many generic ivars", # SEGV
99
+ "move object with complex generic ivars", # SEGV
100
+ "moved composite types move their non-shareable parts properly", # SEGV
101
+ ]
102
+ end
103
+ if Ractor.builtin? && RUBY_VERSION < "3.4"
104
+ # fails on 3.3
105
+ skips += [
106
+ "unshareable frozen objects should still be frozen in new ractor after move",
107
+ "ivar in shareable-objects are not allowed to access from non-main Ractor",
108
+ "ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (get)",
109
+ "ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (set)",
110
+ "defined? on ivar of module",
111
+ "moved objects have their shape properly set to original object's shape",
112
+ "Ractor-local storage with Thread inheritance of current Ractor",
113
+ "require in Ractor",
114
+ "autolaod in Ractor",
115
+ "moved objects being corrupted if embeded (String)",
116
+ "move object with generic ivar",
117
+ "move object with many generic ivars", # SEGV
118
+ "move object with complex generic ivars",
119
+ ]
120
+ end
121
+ if Ractor.builtin? && RUBY_VERSION < "3.3"
122
+ # fails on 3.2
123
+ skips += [
124
+ "check moved object", # SEGV
125
+ "fstring pool 2", # spurious, probably a fstring table bug
126
+ ]
127
+ end
128
+ if Ractor.builtin? && RUBY_VERSION < "3.1"
129
+ # fails on 3.0
130
+ skips += [
131
+ "and instance variables of classes/modules are accessible if they refer shareable objects",
132
+ "define_method is not allowed",
133
+ "check method cache invalidation", # SEGV
134
+ ]
135
+ end
136
+ if Ractor.shim? && RUBY_ENGINE == "ruby" && RUBY_VERSION < "3.0"
137
+ # fails on 2.7
138
+ skips += [
139
+ "check method cache invalidation", # syntax
140
+ ]
141
+ end
142
+ if Ractor.shim? && RUBY_ENGINE == "jruby"
143
+ skips += [
144
+ "ObjectSpace.each_object can not handle unshareable objects with Ractors",
145
+ "fstring pool 2", # spurious, probably a fstring table bug
146
+ ]
147
+ end
148
+ if $DEBUG
149
+ # fails probably Due to Thread.abort_on_exception being true with $DEBUG true
150
+ skips += [
151
+ "an exception in a Ractor non-main thread will not be re-raised at Ractor#receive",
152
+ ]
153
+ end
154
+ SKIPS = skips
155
+
156
+ def new_empty_binding
157
+ # we need a shareable self for shareable procs/lambdas, but also preserve the default definee of Object
158
+ Object.class_eval { binding }
159
+ end
160
+
161
+ def skip(reason = nil)
162
+ throw :skip, :skip
163
+ end
164
+
165
+ TEST_LINES = [""] + File.readlines("#{__dir__}/test_ractor.rb").map(&:chomp)
166
+
167
+ def generic_assert(expected, code, check, &error_message)
168
+ test_info = caller_locations(2, 1)[0]
169
+ line = test_info.lineno
170
+ test_name = TEST_LINES[line-1]
171
+ test_name = TEST_LINES[line-2] if test_name.start_with?("# [Bug")
172
+ warn "Could not find test name for test at line #{line}" unless test_name.start_with?("# ")
173
+ test_name = test_name[2..-1]
174
+
175
+ print "Running test from line #{line}: "
176
+ if SKIPS.include?(line) or SKIPS.include?(test_name)
177
+ puts "SKIP"
178
+ return
179
+ end
180
+
181
+ begin
182
+ actual = catch(:skip) do
183
+ eval(code, new_empty_binding, test_info.path, line).to_s
184
+ end
185
+ return if :skip == actual
186
+ rescue => e
187
+ puts "ERROR"
188
+ e.set_backtrace(e.backtrace.reject { |b|
189
+ b.include?(__FILE__)
190
+ }.map { |b|
191
+ b.sub(/(Object#)?new_empty_binding/, 'test')
192
+ })
193
+ raise e
194
+ end
195
+
196
+ if check.call(actual, expected)
197
+ puts "PASS #{actual}"
198
+ else
199
+ puts "FAIL"
200
+ raise RuntimeError, error_message.call(actual, expected), caller(2)
201
+ end
202
+
203
+ wait_ms = 10
204
+ waited = 0
205
+ while (leaked = Ractor.count - 1) > 0 and waited < wait_ms
206
+ sleep 0.001
207
+ waited += 1
208
+ end
209
+ unless leaked == 0
210
+ raise "Test at line #{line} leaked #{leaked} Ractors"
211
+ end
212
+
213
+ # cleanup global state
214
+ Object.send(:remove_const, :A) if Object.const_defined?(:A)
215
+
216
+ unless Ractor::RemoteError.is_a? Class
217
+ raise "Ractor::RemoteError is no longer a Class!: #{Ractor::RemoteError.inspect}"
218
+ end
219
+ end
220
+
221
+ def assert_equal(expected, code, frozen_string_literal: nil)
222
+ generic_assert(expected, code, -> a, e { e == a }) { |actual, expected|
223
+ "Expected #{expected.inspect} but got #{actual.inspect}"
224
+ }
225
+ end
226
+
227
+ def assert_match(expected, code, frozen_string_literal: nil)
228
+ generic_assert(expected, code, -> a, e { e.match?(a) }) { |actual, expected|
229
+ "Expected #{expected} =~ #{actual.inspect} but it was false"
230
+ }
231
+ end
232
+
233
+ def assert_normal_exit(code)
234
+ # Skip because such tests need to run in their own process
235
+ return
236
+ # generic_assert(:unused, code, -> a, e { true }) { |actual, expected|
237
+ # raise "unreachable"
238
+ # }
239
+ end
240
+
241
+ def yjit_enabled?
242
+ false
243
+ end
244
+
245
+ Warning[:experimental] = false
246
+
247
+ require_relative 'test_ractor'
248
+
249
+ require_relative 'ractor_methods_test'