1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
2
4
3
5
# We don't need to run the expensive parallel tests for every combination of prefix/suffix.
4
6
# Those affect SQL generation, not parallelism.
@@ -16,22 +18,23 @@ def max_threads
16
18
class WorkerBase
17
19
extend Forwardable
18
20
attr_reader :name
21
+
19
22
def_delegators :@thread , :join , :wakeup , :status , :to_s
20
23
21
24
def log ( msg )
22
- puts ( "#{ Thread . current } : #{ msg } " ) if ENV [ ' VERBOSE' ]
25
+ puts ( "#{ Thread . current } : #{ msg } " ) if ENV [ " VERBOSE" ]
23
26
end
24
27
25
28
def initialize ( target , name )
26
29
@target = target
27
30
@name = name
28
31
@thread = Thread . new do
29
32
ActiveRecord ::Base . connection_pool . with_connection { before_work } if respond_to? :before_work
30
- log ' going to sleep...'
33
+ log " going to sleep..."
31
34
sleep
32
- log ' woke up...'
35
+ log " woke up..."
33
36
ActiveRecord ::Base . connection_pool . with_connection { work }
34
- log ' done.'
37
+ log " done."
35
38
end
36
39
end
37
40
end
@@ -45,14 +48,25 @@ def work
45
48
end
46
49
end
47
50
48
- RSpec . describe 'Concurrent creation' do
49
- before :each do
51
+ class SiblingPrependerWorker < WorkerBase
52
+ def before_work
53
+ @target . reload
54
+ @sibling = Label . new ( name : SecureRandom . hex ( 10 ) )
55
+ end
56
+
57
+ def work
58
+ @target . prepend_sibling @sibling
59
+ end
60
+ end
61
+
62
+ describe "Concurrent creation" do
63
+ before do
50
64
@target = nil
51
65
@iterations = 5
52
66
end
53
67
54
68
def log ( msg )
55
- puts ( msg ) if ENV [ ' VERBOSE' ]
69
+ puts ( msg ) if ENV [ " VERBOSE" ]
56
70
end
57
71
58
72
def run_workers ( worker_class = FindOrCreateWorker )
@@ -61,7 +75,7 @@ def run_workers(worker_class = FindOrCreateWorker)
61
75
workers = max_threads . times . map { worker_class . new ( @target , name ) }
62
76
# Wait for all the threads to get ready:
63
77
while true
64
- unready_workers = workers . select { |ea | ea . status != ' sleep' }
78
+ unready_workers = workers . select { |ea | ea . status != " sleep" }
65
79
if unready_workers . empty?
66
80
break
67
81
else
@@ -71,59 +85,57 @@ def run_workers(worker_class = FindOrCreateWorker)
71
85
end
72
86
sleep ( 0.25 )
73
87
# OK, GO!
74
- log ' Calling .wakeup on all workers...'
88
+ log " Calling .wakeup on all workers..."
75
89
workers . each ( &:wakeup )
76
90
sleep ( 0.25 )
77
91
# Then wait for them to finish:
78
- log ' Calling .join on all workers...'
92
+ log " Calling .join on all workers..."
79
93
workers . each ( &:join )
80
94
end
81
95
# Ensure we're still connected:
82
96
ActiveRecord ::Base . connection_pool . connection
83
97
end
84
98
85
- it 'will not create dupes from class methods' do
99
+ it "will not create dupes from class methods" do
100
+ skip ( "unsupported" ) unless run_parallel_tests?
101
+
86
102
run_workers
87
- expect ( Tag . roots . collect { |ea | ea . name } ) . to match_array ( @names )
103
+ assert_equal @names . sort , Tag . roots . collect { |ea | ea . name } . sort
88
104
# No dupe children:
89
- %w( a b c ) . each do |ea |
90
- expect ( Tag . where ( name : ea ) . size ) . to eq ( @iterations )
105
+ %w[ a b c ] . each do |ea |
106
+ assert_equal @iterations , Tag . where ( name : ea ) . size
91
107
end
92
108
end
93
109
94
- it 'will not create dupes from instance methods' do
95
- @target = Tag . create! ( name : 'root' )
96
- run_workers
97
- expect ( @target . reload . children . collect { |ea | ea . name } ) . to match_array ( @names )
98
- expect ( Tag . where ( name : @names ) . size ) . to eq ( @iterations )
99
- %w( a b c ) . each do |ea |
100
- expect ( Tag . where ( name : ea ) . size ) . to eq ( @iterations )
101
- end
102
- end
110
+ it "will not create dupes from instance methods" do
111
+ skip ( "unsupported" ) unless run_parallel_tests?
103
112
104
- it 'creates dupe roots without advisory locks' do
105
- # disable with_advisory_lock:
106
- allow ( Tag ) . to receive ( :with_advisory_lock ) { |_lock_name , &block | block . call }
113
+ @target = Tag . create! ( name : "root" )
107
114
run_workers
108
- # duplication from at least one iteration:
109
- expect ( Tag . where ( name : @names ) . size ) . to be > @iterations
115
+ assert_equal @names . sort , @target . reload . children . collect { |ea | ea . name } . sort
116
+ assert_equal @iterations , Tag . where ( name : @names ) . size
117
+ %w[ a b c ] . each do |ea |
118
+ assert_equal @iterations , Tag . where ( name : ea ) . size
119
+ end
110
120
end
111
121
112
- class SiblingPrependerWorker < WorkerBase
113
- def before_work
114
- @target . reload
115
- @sibling = Label . new ( name : SecureRandom . hex ( 10 ) )
116
- end
122
+ it "creates dupe roots without advisory locks" do
123
+ skip ( "unsupported" ) unless run_parallel_tests?
117
124
118
- def work
119
- @target . prepend_sibling @sibling
125
+ # disable with_advisory_lock:
126
+ Tag . stub ( :with_advisory_lock , -> ( _lock_name , &block ) { block . call } ) do
127
+ run_workers
128
+ # duplication from at least one iteration:
129
+ assert Tag . where ( name : @names ) . size > @iterations
120
130
end
121
131
end
122
132
123
- it 'fails to deadlock while simultaneously deleting items from the same hierarchy' do
133
+ it "fails to deadlock while simultaneously deleting items from the same hierarchy" do
134
+ skip ( "unsupported" ) unless run_parallel_tests?
135
+
124
136
target = User . find_or_create_by_path ( ( 1 ..200 ) . to_a . map { |ea | ea . to_s } )
125
137
emails = target . self_and_ancestors . to_a . map ( &:email ) . shuffle
126
- Parallel . map ( emails , : in_threads => max_threads ) do |email |
138
+ Parallel . map ( emails , in_threads : max_threads ) do |email |
127
139
ActiveRecord ::Base . connection_pool . with_connection do
128
140
User . transaction do
129
141
log "Destroying #{ email } ..."
@@ -132,28 +144,19 @@ def work
132
144
end
133
145
end
134
146
User . connection . reconnect!
135
- expect ( User . all ) . to be_empty
147
+ assert User . all . empty?
136
148
end
137
149
138
- class SiblingPrependerWorker < WorkerBase
139
- def before_work
140
- @target . reload
141
- @sibling = Label . new ( name : SecureRandom . hex ( 10 ) )
142
- end
150
+ it "fails to deadlock from prepending siblings" do
151
+ skip ( "unsupported" ) unless run_parallel_tests?
143
152
144
- def work
145
- @target . prepend_sibling @sibling
146
- end
147
- end
148
-
149
- it 'fails to deadlock from prepending siblings' do
150
- @target = Label . find_or_create_by_path %w( root parent )
153
+ @target = Label . find_or_create_by_path %w[ root parent ]
151
154
run_workers ( SiblingPrependerWorker )
152
155
children = Label . roots
153
156
uniq_order_values = children . collect { |ea | ea . order_value } . uniq
154
- expect ( children . size ) . to eq ( uniq_order_values . size )
157
+ assert_equal uniq_order_values . size , children . size
155
158
156
159
# The only non-root node should be "root":
157
- expect ( Label . all . select { |ea | ea . root? } ) . to eq ( [ @target . parent ] )
160
+ assert_equal ( [ @target . parent ] , Label . all . select { |ea | ea . root? } )
158
161
end
159
- end if run_parallel_tests?
162
+ end
0 commit comments