Class: Scarpe::ControlInterface
- Inherits:
-
Object
- Object
- Scarpe::ControlInterface
show all
- Includes:
- Log, Test::Helpers
- 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 Log
Log::DEFAULT_DEBUG_LOG_CONFIG, 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, #with_env_vars, #with_tempfile, #with_tempfiles
Methods included from Log
#log_init
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
51
52
53
54
55
56
57
58
59
60
61
62
63
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 51
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
169
170
171
172
173
174
175
176
177
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 169
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
179
180
181
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 179
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.
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 148
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
138
139
140
141
142
143
144
145
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 138
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
102
103
104
105
106
107
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 102
def assertions_may_exist
@assertions_pending ||= {}
@assertions_failed ||= {}
@assertions_passed ||= 0
@assertion_counter ||= 0
end
|
#assertions_pending? ⇒ Boolean
134
135
136
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 134
def assertions_pending?
!@assertions_pending.empty?
end
|
#die_after(time) ⇒ Object
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 20
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
128
129
130
131
132
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 128
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.
68
69
70
71
72
73
74
75
76
77
78
79
80
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 68
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
197
198
199
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 197
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
123
124
125
126
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 123
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.
90
91
92
93
94
95
96
97
98
99
100
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 90
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
109
110
111
112
113
114
115
116
117
118
119
120
121
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 109
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
36
37
38
39
40
41
42
43
44
45
46
47
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 36
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
16
17
18
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 16
def timed_out?
@did_time_out
end
|
#with_js_dom_html(wait_for: [], timeout: DEFAULT_ASSERTION_TIMEOUT, &block) ⇒ Object
193
194
195
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 193
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?
184
185
186
187
188
189
190
191
|
# File 'lib/scarpe/wv/control_interface_test.rb', line 184
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
|