Class: Scarpe::Test::CCInstance

Inherits:
Object
  • Object
show all
Includes:
EventedAssertions, Helpers, Shoes::Log
Defined in:
lib/scarpe/cats_cradle.rb

Overview

This class defines the CatsCradle DSL. It also holds a "bag of fibers" with promises for when they should next resume.

Constant Summary collapse

EVENT_TYPES =

If we add "every" events, that's likely to complicate timing and event_promise handling.

[:next_heartbeat, :next_redraw]

Constants included from Shoes::Log

Shoes::Log::DEFAULT_COMPONENT, Shoes::Log::DEFAULT_DEBUG_LOG_CONFIG, Shoes::Log::DEFAULT_LOG_CONFIG

Class Method Summary collapse

Instance Method Summary collapse

Methods included from EventedAssertions

#assert_html, #assert_include, #assert_not_include, #return_results

Methods included from Shoes::Log

configure_logger, #log_init, logger

Constructor Details

#initializeCCInstance

Returns a new instance of CCInstance.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/scarpe/cats_cradle.rb', line 49

def initialize
  log_init("CatsCradle")

  @assertion_data = []
  @assertions_passed = 0
  @assertions_failed = []

  @waiting_fibers = []
  @event_promises = {}

  @manager_fiber = Fiber.new do
    loop do
      # A fiber can run briefly and then exit. It can run and then block on an API call.
      # These fibers return promises to indicate to CatsCradle when they can run again.
      # A fiber that is no longer #alive? is assumed to be successfully finished.
      @waiting_fibers.each do |fiber_data|
        next unless fiber_data[:promise].fulfilled?

        @log.debug("Resuming fiber with value #{fiber_data[:promise].returned_value.inspect}")
        result = fiber_data[:fiber].transfer fiber_data[:promise].returned_value

        # Dead fibers will be removed later, just leave it
        next unless fiber_data[:fiber].alive?

        case result
        when ::Scarpe::Promise
          fiber_data[:promise] = result
        else
          raise "Unexpected object returned from Fiber#transfer for still-living Fiber! #{result.inspect}"
        end
      end

      # Throw out dead fibers or those that will never wake
      @waiting_fibers.select! do |fiber_data|
        fiber_data[:fiber].alive? && !fiber_data[:promise].rejected?
      end

      # Done with this iteration
      Fiber.yield
    end
  end
end

Class Method Details

.instanceObject



45
46
47
# File 'lib/scarpe/cats_cradle.rb', line 45

def self.instance
  @instance ||= CCInstance.new
end

Instance Method Details

#assert(value, msg = nil) ⇒ Object



184
185
186
187
188
189
190
191
192
# File 'lib/scarpe/cats_cradle.rb', line 184

def assert(value, msg = nil)
  msg ||= "Assertion #{value ? "succeeded" : "failed"}"
  @assertion_data << [value ? true : false, msg]
  if value
    @assertions_passed += 1
  else
    @assertions_failed << msg
  end
end

#assert_equal(expected, actual, msg = nil) ⇒ Object



194
195
196
197
# File 'lib/scarpe/cats_cradle.rb', line 194

def assert_equal(expected, actual, msg = nil)
  msg ||= "Expected #{actual.inspect} to equal #{expected.inspect}!"
  assert actual == expected, msg
end

#assertion_data_as_a_structObject



199
200
201
202
203
204
205
206
# File 'lib/scarpe/cats_cradle.rb', line 199

def assertion_data_as_a_struct
  {
    still_pending: 0,
    succeeded: @assertions_passed,
    failed: @assertions_failed.size,
    failures: @assertions_failed,
  }
end

#dom_html(timeout: 1.0) ⇒ Object



173
174
175
# File 'lib/scarpe/cats_cradle.rb', line 173

def dom_html(timeout: 1.0)
  query_js_value("document.getElementById('wrapper-wvroot').innerHTML", timeout:)
end

#event_initObject

This needs to be called after the basic display service objects exist and we can find the control interface.



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
# File 'lib/scarpe/cats_cradle.rb', line 97

def event_init
  return if @cc_init_done

  @cc_init_done = true

  @control_interface = ::Shoes::DisplayService.display_service.control_interface
  @wrangler = @control_interface.wrangler

  cc_instance = self # ControlInterface#on_event does an instance eval. We'll reset self with another.

  @control_interface.on_event(:every_heartbeat) do
    cc_instance.instance_eval do
      p = @event_promises.delete(:next_heartbeat)
      p&.fulfilled!

      # Give every ready fiber a chance to run once.
      @manager_fiber.resume
    end
  end

  @control_interface.on_event(:every_redraw) do
    cc_instance.instance_eval do
      p = @event_promises.delete(:next_redraw)
      p&.fulfilled!

      # Give every ready fiber a chance to run once.
      @manager_fiber.resume
    end
  end
end

#event_promise(event) ⇒ Object



128
129
130
# File 'lib/scarpe/cats_cradle.rb', line 128

def event_promise(event)
  @event_promises[event] ||= ::Scarpe::Promise.new
end

#fully_updatedObject

This returns a promise, which can be waited on using wait()



169
170
171
# File 'lib/scarpe/cats_cradle.rb', line 169

def fully_updated
  @wrangler.promise_dom_fully_updated
end

#on_event(event, &block) ⇒ Object



132
133
134
135
136
137
138
139
# File 'lib/scarpe/cats_cradle.rb', line 132

def on_event(event, &block)
  raise "Unknown event type: #{event.inspect}!" unless EVENT_TYPES.include?(event)

  f = Fiber.new do
    CCInstance.instance.instance_eval(&block)
  end
  @waiting_fibers << { promise: event_promise(event), fiber: f }
end

#proxy_for(shoes_widget) ⇒ Object



157
158
159
# File 'lib/scarpe/cats_cradle.rb', line 157

def proxy_for(shoes_widget)
  CCProxy.new(shoes_widget)
end

#query_js_value(js_code, timeout: 1.0) ⇒ Object



177
178
179
180
181
182
# File 'lib/scarpe/cats_cradle.rb', line 177

def query_js_value(js_code, timeout: 1.0)
  js_promise = @wrangler.eval_js_async(js_code, timeout:)

  # This promise will return the string, so we can just pass it to #transfer
  @manager_fiber.transfer(js_promise)
end

#test_finishedObject



212
213
214
215
216
217
218
219
220
221
# File 'lib/scarpe/cats_cradle.rb', line 212

def test_finished
  if !@assertions_failed.empty?
    return_results(false, "Assertions failed", assertion_data_as_a_struct)
  elsif @assertions_passed > 0
    return_results(true, "All assertions passed", assertion_data_as_a_struct)
  else
    return_results(true, "Test finished successfully")
  end
  ::Shoes::DisplayService.dispatch_event("destroy", nil)
end

#test_metadataObject



208
209
210
# File 'lib/scarpe/cats_cradle.rb', line 208

def 
  {}
end

#wait(promise) ⇒ Object



161
162
163
164
165
166
# File 'lib/scarpe/cats_cradle.rb', line 161

def wait(promise)
  raise("Must supply a promise to wait!") unless promise.is_a?(::Scarpe::Promise)

  # Wait until this promise is complete before running again
  @manager_fiber.transfer(promise)
end