Class: Scarpe::ControlInterface
- Inherits:
-
Object
- Object
- Scarpe::ControlInterface
show all
- Includes:
- Test::EventedAssertions, Test::Helpers, Shoes::Log
- Defined in:
- lib/scarpe/wv/control_interface.rb,
lib/scarpe/wv/control_interface_test.rb
Constant Summary
collapse
- SUBSCRIBE_EVENTS =
[:init, :shutdown, :next_redraw, :every_redraw, :next_heartbeat, :every_heartbeat]
- DISPATCH_EVENTS =
[:init, :shutdown, :redraw, :heartbeat]
Constants included
from Shoes::Log
Shoes::Log::DEFAULT_COMPONENT, Shoes::Log::DEFAULT_DEBUG_LOG_CONFIG, Shoes::Log::DEFAULT_LOG_CONFIG
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#all_wv_widgets ⇒ Object
Need to be able to query widgets in test code.
-
#app ⇒ Object
-
#assert(value, msg = nil) ⇒ Object
-
#assert_equal(val1, val2, msg = nil) ⇒ Object
-
#assert_js(js_code, wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT) ⇒ Object
Create a promise to do a JS assertion, normally after other ops have finished.
-
#assertion_data_as_a_struct ⇒ Object
-
#assertions_may_exist ⇒ Object
-
#assertions_pending? ⇒ Boolean
-
#die_after(time) ⇒ Object
-
#dispatch_event(event, *args, **keywords) ⇒ Object
Send out the specified event.
-
#fail_assertion(id, fail_message) ⇒ Object
-
#find_wv_widgets(*specifiers) ⇒ Object
Shoes doesn't name widgets.
-
#fully_updated(wait_for: []) ⇒ Object
-
#initialize ⇒ ControlInterface
constructor
The control interface needs to see major system components to hook into their events.
-
#inspect ⇒ Object
-
#on_event(event, &block) ⇒ Object
On recognised events, this sets a handler for that event.
-
#pass_assertion(id) ⇒ Object
-
#return_when_assertions_done ⇒ Object
Note that we do not extract this assertions library to use elsewhere because it's very focused on evented assertions that start and stop over a period of time.
-
#set_system_components(app:, doc_root:, wrangler:) ⇒ Object
This should get called once, from Shoes::App.
-
#start_assertion(code) ⇒ Object
-
#test_metadata ⇒ Object
This is returned alongside the actual results automatically.
-
#timed_out? ⇒ Boolean
-
#with_js_dom_html(wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT, &block) ⇒ Object
-
#with_js_value(js_code, wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT, &block) ⇒ Object
How do we signal an error?.
-
#wrangler ⇒ Object
#assert_html, #assert_include, #assert_not_include, #return_results
Methods included from Shoes::Log
configure_logger, #log_init, logger
Constructor Details
The control interface needs to see major system components to hook into their events
24
25
26
27
28
29
30
|
# File 'lib/scarpe/wv/control_interface.rb', line 24
def initialize
log_init("WV::ControlInterface")
@do_shutdown = false
@event_handlers = {}
(SUBSCRIBE_EVENTS | DISPATCH_EVENTS).each { |e| @event_handlers[e] = [] }
end
|
Instance Attribute Details
#do_shutdown ⇒ Object
Returns the value of attribute do_shutdown.
21
22
23
|
# File 'lib/scarpe/wv/control_interface.rb', line 21
def do_shutdown
@do_shutdown
end
|
#doc_root ⇒ Object
61
62
63
64
65
66
67
68
|
# File 'lib/scarpe/wv/control_interface.rb', line 61
def doc_root
unless @doc_root
raise "ControlInterface code needs to be wrapped in handlers like on_event(:init) " +
"to make sure they have access to app, doc_root, wrangler, etc!"
end
@doc_root
end
|
Instance Method Details
Need to be able to query widgets in test code
55
56
57
58
59
60
61
62
63
64
65
66
67
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 55
def all_wv_widgets
known = [doc_root]
to_check = [doc_root]
until to_check.empty?
next_layer = to_check.flat_map(&:children)
known += next_layer
to_check = next_layer
end
known.uniq
end
|
#app ⇒ Object
52
53
54
55
56
57
58
59
|
# File 'lib/scarpe/wv/control_interface.rb', line 52
def app
unless @app
raise "ControlInterface code needs to be wrapped in handlers like on_event(:init) " +
"to make sure they have access to app, doc_root, wrangler, etc!"
end
@app
end
|
#assert(value, msg = nil) ⇒ Object
173
174
175
176
177
178
179
180
181
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 173
def assert(value, msg = nil)
id = start_assertion("#{caller[0]}: #{msg || "Value should be true!"}")
if value
pass_assertion(id)
else
fail_assertion(id, "Expected true Ruby value: #{value.inspect}")
end
end
|
#assert_equal(val1, val2, msg = nil) ⇒ Object
183
184
185
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 183
def assert_equal(val1, val2, msg = nil)
assert val1 == val2, (msg || "Expected #{val2.inspect} to equal #{val1.inspect}!")
end
|
#assert_js(js_code, wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT) ⇒ Object
Create a promise to do a JS assertion, normally after other ops have finished.
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 152
def assert_js(js_code, wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT)
id = start_assertion(js_code)
promise = wrangler.eval_js_async(js_code, wait_for: wait_for, timeout: timeout)
promise.on_rejected do
fail_assertion(id, "JS Eval failed: #{promise.reason.inspect}")
end
promise.on_fulfilled do
ret_val = promise.returned_value
if ret_val
pass_assertion(id)
else
fail_assertion(id, "Expected true JS value: #{ret_val.inspect}")
end
end
TestPromise.new(iface: self, wait_for: [promise]).to_execute {}
end
|
#assertion_data_as_a_struct ⇒ Object
142
143
144
145
146
147
148
149
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 142
def assertion_data_as_a_struct
{
still_pending: @assertions_pending.size,
succeeded: @assertions_passed,
failed: @assertions_failed.size,
failures: @assertions_failed.values.map { |item| [item[:code], item[:failure_reason]] },
}
end
|
#assertions_may_exist ⇒ Object
106
107
108
109
110
111
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 106
def assertions_may_exist
@assertions_pending ||= {}
@assertions_failed ||= {}
@assertions_passed ||= 0
@assertion_counter ||= 0
end
|
#assertions_pending? ⇒ Boolean
138
139
140
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 138
def assertions_pending?
!@assertions_pending.empty?
end
|
#die_after(time) ⇒ Object
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 24
def die_after(time)
t_start = Time.now
@die_after = [t_start, time]
wrangler.periodic_code("scarpeTestTimeout") do |*_args|
t_delta = (Time.now - t_start).to_f
if t_delta > time
@did_time_out = true
@log.warn("die_after - timed out after #{t_delta.inspect} (threshold: #{time.inspect})")
return_results(false, "Timed out!")
app.destroy
end
end
end
|
#dispatch_event(event, *args, **keywords) ⇒ Object
Send out the specified event
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
# File 'lib/scarpe/wv/control_interface.rb', line 96
def dispatch_event(event, *args, **keywords)
@log.debug("CTL event #{event.inspect} #{args.inspect} #{keywords.inspect}")
unless DISPATCH_EVENTS.include?(event)
raise "Illegal dispatch of event #{event.inspect}! Valid values are: #{DISPATCH_EVENTS.inspect}"
end
if @do_shutdown
@log.debug("CTL: Shutting down - not dispatching #{event}!")
return
end
if event == :redraw
dumb_dispatch_event(:every_redraw, *args, **keywords)
handlers = @event_handlers[:next_redraw]
dumb_dispatch_event(:next_redraw, *args, **keywords)
@event_handlers[:next_redraw] -= handlers
return
end
if event == :heartbeat
dumb_dispatch_event(:every_heartbeat, *args, **keywords)
handlers = @event_handlers[:next_heartbeat]
dumb_dispatch_event(:next_heartbeat, *args, **keywords)
@event_handlers[:next_heartbeat] -= handlers
return
end
if event == :shutdown
@do_shutdown = true
end
dumb_dispatch_event(event, *args, **keywords)
end
|
#fail_assertion(id, fail_message) ⇒ Object
132
133
134
135
136
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 132
def fail_assertion(id, fail_message)
item = @assertions_pending.delete(id)
item[:fail_message] = fail_message
@assertions_failed[id] = item
end
|
Shoes doesn't name widgets. We aren't guaranteed that the Shoes widgets are even in the same
process, since we have the Relay display service for Webview. So mostly we can look by
display service class.
72
73
74
75
76
77
78
79
80
81
82
83
84
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 72
def find_wv_widgets(*specifiers)
widgets = all_wv_widgets
specifiers.each do |spec|
if spec.is_a?(Class)
widgets.select! { |w| spec === w }
else
raise "I don't know how to search for widgets by #{spec.inspect}!"
end
end
widgets
end
|
#fully_updated(wait_for: []) ⇒ Object
201
202
203
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 201
def fully_updated(wait_for: [])
wrangler.promise_dom_fully_updated
end
|
#inspect ⇒ Object
32
33
34
|
# File 'lib/scarpe/wv/control_interface.rb', line 32
def inspect
"<#ControlInterface>"
end
|
#on_event(event, &block) ⇒ Object
On recognised events, this sets a handler for that event
83
84
85
86
87
88
89
90
91
92
93
|
# File 'lib/scarpe/wv/control_interface.rb', line 83
def on_event(event, &block)
unless SUBSCRIBE_EVENTS.include?(event)
raise "Illegal subscribe to event #{event.inspect}! Valid values are: #{SUBSCRIBE_EVENTS.inspect}"
end
@unsub_id ||= 0
@unsub_id += 1
@event_handlers[event] << { handler: block, unsub: @unsub_id }
@unsub_id
end
|
#pass_assertion(id) ⇒ Object
127
128
129
130
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 127
def pass_assertion(id)
@assertions_pending.delete(id)
@assertions_passed += 1
end
|
#return_when_assertions_done ⇒ Object
Note that we do not extract this assertions library to use elsewhere
because it's very focused on evented assertions that start and stop
over a period of time. Instantaneous procedural asserts don't want to
use this API.
94
95
96
97
98
99
100
101
102
103
104
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 94
def return_when_assertions_done
assertions_may_exist
wrangler.periodic_code("scarpeReturnWhenAssertionsDone") do |*_args|
if @assertions_pending.empty?
success = @assertions_failed.empty?
return_results success, "Assertions #{success ? "succeeded" : "failed"}", assertion_data_as_a_struct
app.destroy
end
end
end
|
#set_system_components(app:, doc_root:, wrangler:) ⇒ Object
This should get called once, from Shoes::App
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
# File 'lib/scarpe/wv/control_interface.rb', line 37
def set_system_components(app:, doc_root:, wrangler:)
unless app && wrangler
@log.error("False app passed to set_system_components!") unless app
@log.error("False wrangler passed to set_system_components!") unless wrangler
raise "Must pass non-nil app and wrangler to ControlInterface#set_system_components!"
end
@app = app
@doc_root = doc_root
@wrangler = wrangler
@wrangler.control_interface = self
@wrangler.on_every_redraw { self.dispatch_event(:redraw) }
end
|
#start_assertion(code) ⇒ Object
113
114
115
116
117
118
119
120
121
122
123
124
125
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 113
def start_assertion(code)
assertions_may_exist
this_assertion = @assertion_counter
@assertion_counter += 1
@assertions_pending[this_assertion] = {
id: this_assertion,
code: code,
}
this_assertion
end
|
This is returned alongside the actual results automatically
40
41
42
43
44
45
46
47
48
49
50
51
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 40
def test_metadata
data = {}
if @die_after
t_delta = (Time.now - @die_after[0]).to_f
data["die_after"] = {
t_start: @die_after[0].to_s,
threshold: @die_after[1],
passed: t_delta,
}
end
data
end
|
#timed_out? ⇒ Boolean
20
21
22
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 20
def timed_out?
@did_time_out
end
|
#with_js_dom_html(wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT, &block) ⇒ Object
197
198
199
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 197
def with_js_dom_html(wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT, &block)
with_js_value("document.getElementById('wrapper-wvroot').innerHTML", wait_for: wait_for, timeout: timeout, &block)
end
|
#with_js_value(js_code, wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT, &block) ⇒ Object
How do we signal an error?
188
189
190
191
192
193
194
195
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 188
def with_js_value(js_code, wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT, &block)
raise "Must give a block to with_js_value!" unless block
js_promise = wrangler.eval_js_async(js_code, wait_for: wait_for, timeout: timeout)
ruby_promise = TestPromise.new(iface: self, wait_for: [js_promise])
ruby_promise.to_execute(&block)
ruby_promise
end
|
#wrangler ⇒ Object
70
71
72
73
74
75
76
77
|
# File 'lib/scarpe/wv/control_interface.rb', line 70
def wrangler
unless @wrangler
raise "ControlInterface code needs to be wrapped in handlers like on_event(:init) " +
"to make sure they have access to app, doc_root, wrangler, etc!"
end
@wrangler
end
|