rspec-openhab-scripting 0.0.10-java → 0.0.11-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec06e2cfb8832f84f5bd953e22904dbf99ec22a97e5ba0b900ee0e09a163a566
4
- data.tar.gz: 959d078c55a2147e8b1899a8efabc6e8d8065fa9c869cda1c1d5ff057784de3c
3
+ metadata.gz: 3ef6a42e87b4466cf53972b65523d3a810060cbeac84497796ab4514b377d445
4
+ data.tar.gz: 0b3be611dddf1a4e5643cdb48fd3f8a05d35eda32e50e9b4062a063f49f6ad17
5
5
  SHA512:
6
- metadata.gz: d806b85fd33ce2c42b09f5442bd7dc752327a21c1aae8df5fa57b615dd4935a96c62274ffab8d5dcad9bc94fe034c3073d0af20221cd59e647e00568ae7acc64
7
- data.tar.gz: a2565829aa4ddd62cdd1e3ac34e23f55ec9794dcbdeac806bb7175c546f2dcca483cddd72116b1f7726d249c983b7407939de8906e0ec442320d2f2b7b515947
6
+ metadata.gz: 0ffaf54c3e64d9a4f1be5ff0ecec757976db0e5ebbddec2d916d254fb9f9f730f3e1e1245fc478a970e8e42509613877ac535f9983aa94288cbb9c1f7abfa826
7
+ data.tar.gz: 3333117270aa1e6b2de597ad9570df51a9f829d0c5da345cdbb1aed5f50d5bf841cdd1b9a140a94cf06695982acce024280ec814f4cbe5b46ab94dce88566b40
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "singleton"
4
+
3
5
  require "openhab/core/osgi"
4
6
 
5
7
  module OpenHAB
@@ -20,6 +22,7 @@ module OpenHAB
20
22
 
21
23
  def initialize(event_manager)
22
24
  @event_manager = event_manager
25
+ @pending_events = nil
23
26
  end
24
27
 
25
28
  def post_event(event)
@@ -27,7 +30,17 @@ module OpenHAB
27
30
  end
28
31
 
29
32
  def send_event(event)
33
+ if @pending_events
34
+ @pending_events << event
35
+ return
36
+ end
37
+
38
+ @pending_events = []
30
39
  @event_manager.handle_event(event)
40
+
41
+ @event_manager.handle_event(@pending_events.shift) until @pending_events.empty?
42
+
43
+ @pending_events = nil
31
44
  end
32
45
  end
33
46
 
@@ -123,6 +136,119 @@ module OpenHAB
123
136
  field_reader :typedEventFactories, :typedEventSubscribers
124
137
  end
125
138
 
139
+ # reimplement to not use a thread
140
+ class EventHandler
141
+ def initialize(typed_event_subscribers, typed_event_factories)
142
+ @typed_event_subscribers = typed_event_subscribers
143
+ @typed_event_factories = typed_event_factories
144
+ end
145
+
146
+ def handle_event(osgi_event)
147
+ type = osgi_event.get_property("type")
148
+ payload = osgi_event.get_property("payload")
149
+ topic = osgi_event.get_property("topic")
150
+ source = osgi_event.get_property("source")
151
+
152
+ if type.is_a?(String) && payload.is_a?(String) && topic.is_a?(String)
153
+ handle_event_internal(type, payload, topic, source) unless type.empty? || payload.empty? || topic.empty?
154
+ else
155
+ logger.error("The handled OSGi event is invalid. " \
156
+ "Expect properties as string named 'type', 'payload', and 'topic'. " \
157
+ "Received event properties are: #{properties.keys.inspect}")
158
+ end
159
+ end
160
+
161
+ private
162
+
163
+ def handle_event_internal(type, payload, topic, source)
164
+ event_factory = @typed_event_factories[type]
165
+ unless event_factory
166
+ logger.debug("Could not find an Event Factory for the event type '#{type}'.")
167
+ return
168
+ end
169
+
170
+ event_subscribers = event_subscribers(type)
171
+ return if event_subscribers.empty?
172
+
173
+ event = create_event(event_factory, type, payload, topic, source)
174
+ return unless event
175
+
176
+ dispatch_event(event_subscribers, event)
177
+ end
178
+
179
+ def event_subscribers(event_type)
180
+ event_type_subscribers = @typed_event_subscribers[event_type]
181
+ all_event_type_subscribers = @typed_event_subscribers["ALL"]
182
+
183
+ subscribers = java.util.HashSet.new
184
+ subscribers.add_all(event_type_subscribers) if event_type_subscribers
185
+ subscribers.add_all(all_event_type_subscribers) if all_event_type_subscribers
186
+ subscribers
187
+ end
188
+
189
+ def create_event(event_factory, type, payload, topic, source)
190
+ event_factory.create_event(type, topic, payload, source)
191
+ rescue Exception => e
192
+ logger.warn("Creation of event failed, because one of the " \
193
+ "registered event factories has thrown an exception: #{e}")
194
+ nil
195
+ end
196
+
197
+ def dispatch_event(event_subscribers, event)
198
+ event_subscribers.each do |event_subscriber|
199
+ filter = event_subscriber.event_filter
200
+ if filter.nil? || filter.apply(event)
201
+ begin
202
+ event_subscriber.receive(event)
203
+ rescue Exception => e
204
+ logger.warn("Dispatching/filtering event for subscriber '#{event_subscriber.class}' failed: #{e}")
205
+ end
206
+ else
207
+ logger.trace("Skip event subscriber (#{event_subscriber.class}) because of its filter.")
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ org.openhab.core.automation.internal.TriggerHandlerCallbackImpl.field_accessor :executor
214
+ org.openhab.core.automation.internal.TriggerHandlerCallbackImpl.field_reader :ruleUID
215
+ class SynchronousExecutor
216
+ include java.util.concurrent.ScheduledExecutorService
217
+ include Singleton
218
+
219
+ def submit(runnable)
220
+ runnable.run
221
+
222
+ java.util.concurrent.CompletableFuture.completed_future(nil)
223
+ end
224
+
225
+ def execute(runnable)
226
+ runnable.run
227
+ end
228
+
229
+ def shutdown_now; end
230
+
231
+ def shutdown?
232
+ false
233
+ end
234
+ end
235
+
236
+ class CallbacksMap < java.util.HashMap
237
+ def put(_rule_uid, trigger_handler)
238
+ trigger_handler.executor.shutdown_now
239
+ trigger_handler.executor = SynchronousExecutor.instance
240
+ super
241
+ end
242
+ end
243
+
244
+ class SynchronousExecutorMap
245
+ include java.util.Map
246
+
247
+ def get(_key)
248
+ SynchronousExecutor.instance
249
+ end
250
+ end
251
+
126
252
  @imported = false
127
253
 
128
254
  class << self
@@ -133,18 +259,13 @@ module OpenHAB
133
259
 
134
260
  @imported = true
135
261
 
136
- # some background java threads get created; kill them at_exit
137
- at_exit do
138
- status = 0
139
- status = $!.status if $!.is_a?(SystemExit)
140
- exit!(status)
141
- end
262
+ org.openhab.core.common.ThreadPoolManager.field_accessor :pools
263
+ org.openhab.core.common.ThreadPoolManager.pools = SynchronousExecutorMap.new
142
264
 
143
265
  # OSGiEventManager will create a ThreadedEventHandler on OSGi activation;
144
266
  # we're skipping that, and directly sending to a non-threaded event handler.
145
267
  em = EventManager.new
146
- eh = org.openhab.core.internal.events.EventHandler.new(em.typedEventSubscribers, em.typedEventFactories)
147
- at_exit { eh.close }
268
+ eh = EventHandler.new(em.typedEventSubscribers, em.typedEventFactories)
148
269
  ea = EventAdmin.new(eh)
149
270
  ep = org.openhab.core.internal.events.OSGiEventPublisher.new(ea)
150
271
  bc = BundleContext.new(em)
@@ -234,8 +355,7 @@ module OpenHAB
234
355
  $se = $scriptExtension = sew
235
356
 
236
357
  # need to create some singletons referencing registries
237
- scheduler = org.openhab.core.internal.scheduler.SchedulerImpl.new
238
- org.openhab.core.model.script.ScriptServiceUtil.new(ir, tr, ep, nil, scheduler)
358
+ org.openhab.core.model.script.ScriptServiceUtil.new(ir, tr, ep, nil, nil)
239
359
  org.openhab.core.model.script.internal.engine.action.SemanticsActionService.new(ir)
240
360
 
241
361
  # link up event bus infrastructure
@@ -265,6 +385,12 @@ module OpenHAB
265
385
 
266
386
  rs = org.openhab.core.internal.service.ReadyServiceImpl.new
267
387
  re = org.openhab.core.automation.internal.RuleEngineImpl.new(mtr, rr, ss, rs)
388
+ # overwrite thCallbacks to one that will spy to remove threading
389
+ field = re.class.java_class.declared_field("thCallbacks")
390
+ field.accessible = true
391
+ field.set(re, CallbacksMap.new)
392
+ re.class.field_accessor :executor
393
+ re.executor = SynchronousExecutor.instance
268
394
  re.add_module_handler_factory(cmhf)
269
395
  re.add_module_handler_factory(scmhf)
270
396
  re.add_module_handler_factory(spmhf)
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module DSL
5
+ class Timer
6
+ module MockedZonedDateTime
7
+ def now
8
+ mocked_time_stack_item = Timecop.top_stack_item
9
+ return super unless mocked_time_stack_item
10
+
11
+ instant = java.time.Instant.of_epoch_milli((Time.now.to_f * 1000).to_i)
12
+ ZonedDateTime.of_instant(instant, java.time.ZoneId.system_default)
13
+ end
14
+ end
15
+ ZonedDateTime.singleton_class.prepend(MockedZonedDateTime)
16
+
17
+ # extend Timecop to support java time classes
18
+ module TimeCopStackItem
19
+ def parse_time(*args)
20
+ if args.length == 1 && args.first.is_a?(Duration)
21
+ return time_klass.at(ZonedDateTime.now.plus(args.first).to_instant.to_epoch_milli / 1000.0)
22
+ end
23
+
24
+ super
25
+ end
26
+ end
27
+ Timecop::TimeStackItem.prepend(TimeCopStackItem)
28
+
29
+ attr_reader :execution_time
30
+
31
+ def initialize(duration:, thread_locals: {}, &block) # rubocop:disable Lint/UnusedMethodArgument
32
+ @block = block
33
+ reschedule(duration)
34
+ end
35
+
36
+ def reschedule(duration = nil)
37
+ @duration = duration || @duration
38
+ @execution_time = ZonedDateTime.now.plus(@duration)
39
+ @executed = @cancelled = false
40
+
41
+ Timers.timer_manager.add(self)
42
+ end
43
+
44
+ def execute
45
+ raise "Timer already cancelled" if cancelled?
46
+ raise "Timer already executed" if terminated?
47
+
48
+ @block.call
49
+ Timers.timer_manager.delete(self)
50
+ @executed = true
51
+ end
52
+
53
+ def cancel
54
+ Timers.timer_manager.delete(self)
55
+ @executed = false
56
+ @cancelled = true
57
+ true
58
+ end
59
+
60
+ def cancelled?
61
+ @cancelled
62
+ end
63
+
64
+ def terminated?
65
+ @executed || @cancelled
66
+ end
67
+
68
+ def running?
69
+ active? && @execution_time > ZonedDateTime.now
70
+ end
71
+
72
+ def active?
73
+ !terminated?
74
+ end
75
+ end
76
+
77
+ module Support
78
+ class TimerManager
79
+ def execute_timers
80
+ @timers.each { |t| t.execute if t.active? && t.execution_time < ZonedDateTime.now }
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module OpenHAB
5
+ module Helpers
6
+ def autoupdate_all_items
7
+ @autoupdated_items ||= {}
8
+ $ir.for_each do |_provider, item|
9
+ @autoupdated_items[item] = item.meta.delete("autoupdate") if item.meta.key?("autoupdate")
10
+ end
11
+ end
12
+
13
+ def execute_timers
14
+ OpenHAB::DSL::Timers.timer_manager.execute_timers
15
+ end
16
+
17
+ def suspend_rules(&block)
18
+ SuspendRules.suspend_rules(&block)
19
+ end
20
+
21
+ def trigger_rule(rule_name, event = nil)
22
+ @rules ||= ::OpenHAB::DSL::Rules::Rule.script_rules.each_with_object({}) { |r, obj| obj[r.name] = r }
23
+
24
+ @rules.fetch(rule_name).execute(nil, { "event" => event })
25
+ end
26
+
27
+ private
28
+
29
+ def restore_autoupdate_items
30
+ return unless instance_variable_defined?(:@autoupdated_items)
31
+
32
+ @autoupdated_items.each do |(item, meta)|
33
+ item.meta["autoupdate"] = meta
34
+ end
35
+ end
36
+ end
37
+
38
+ RSpec.configure do |config|
39
+ config.include Helpers
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:all) { OpenHAB::DSL::Timers.timer_manager.cancel_all }
5
+
6
+ config.before do
7
+ suspend_rules do
8
+ $ir.for_each do |_provider, item|
9
+ item.state = NULL unless item.raw_state == NULL
10
+ end
11
+ end
12
+ end
13
+ config.after do
14
+ OpenHAB::DSL::Timers.timer_manager.cancel_all
15
+ Timecop.return
16
+ restore_autoupdate_items
17
+ end
18
+ end
@@ -49,30 +49,6 @@ module RSpec
49
49
  end
50
50
  end
51
51
  end
52
-
53
- def autoupdate_all_items
54
- @autoupdated_items ||= {}
55
- $ir.for_each do |_provider, item|
56
- @autoupdated_items[item] = item.meta.delete("autoupdate") if item.meta.key?("autoupdate")
57
- end
58
- end
59
-
60
- private
61
-
62
- def restore_autoupdate_items
63
- return unless instance_variable_defined?(:@autoupdated_items)
64
-
65
- @autoupdated_items.each do |(item, meta)|
66
- item.meta["autoupdate"] = meta
67
- end
68
- end
69
-
70
- ::RSpec.configure do |config|
71
- config.include(self)
72
- config.after do
73
- restore_autoupdate_items
74
- end
75
- end
76
52
  end
77
53
  end
78
54
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module OpenHAB
5
+ module SuspendRules
6
+ # I'd prefer to prepend a module, but I can't because of
7
+ # https://github.com/jruby/jruby/issues/6966#issuecomment-1172983776
8
+ class ::OpenHAB::DSL::Rules::AutomationRule # rubocop:disable Style/ClassAndModuleChildren
9
+ def execute(mod = nil, inputs = nil)
10
+ if SuspendRules.suspended?
11
+ logger.debug("Skipping execution of #{name} because rules are suspended.")
12
+ return
13
+ end
14
+
15
+ # super
16
+ ::OpenHAB::DSL.import_presets
17
+ thread_local(RULE_NAME: name) do
18
+ logger.trace { "Execute called with mod (#{mod&.to_string}) and inputs (#{inputs.inspect})" }
19
+ logger.trace { "Event details #{inputs["event"].inspect}" } if inputs&.key?("event")
20
+ trigger_conditions(inputs).process(mod: mod, inputs: inputs) do
21
+ process_queue(create_queue(inputs), mod, inputs)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ # private_constant :AutomationRule
27
+ # ::OpenHAB::DSL::Rules::AutomationRule.prepend(AutomationRule)
28
+
29
+ module Timers
30
+ def after(*)
31
+ return if SuspendRules.suspended?
32
+
33
+ super
34
+ end
35
+ end
36
+ private_constant :Timers
37
+ ::Object.prepend(Timers)
38
+
39
+ @suspended = false
40
+
41
+ class << self
42
+ def suspend_rules
43
+ old_suspended = @suspended
44
+ @suspended = true
45
+ yield
46
+ ensure
47
+ @suspended = old_suspended
48
+ end
49
+
50
+ def suspended?
51
+ @suspended
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenHAB
5
- VERSION = "0.0.10"
5
+ VERSION = "0.0.11"
6
6
  end
7
7
  end
@@ -57,10 +57,13 @@ def oh.config_folder
57
57
  end
58
58
 
59
59
  # global variables need to be set up before openhab-scripting loads
60
+ require "timecop"
60
61
  require "openhab/log/logger"
61
62
  require "rspec/openhab/core/logger"
63
+
62
64
  # during testing, we don't want "regular" output from rules
63
65
  OpenHAB::Log.logger("org.openhab.automation.jruby.runtime").level = :warn
66
+ OpenHAB::Log.logger("org.openhab.automation.jruby.logger").level = :warn
64
67
  require "openhab/dsl/imports"
65
68
  OpenHAB::DSL::Imports.api = api
66
69
  OpenHAB::DSL::Imports.import_presets
@@ -70,19 +73,24 @@ require "openhab"
70
73
  require "rspec/openhab/actions"
71
74
  require "rspec/openhab/core/cron_scheduler"
72
75
 
76
+ # override several timer methods
77
+ require_relative "rspec/openhab/dsl/timers/timer"
78
+
73
79
  # RSpec additions
74
80
  require "rspec/core"
75
81
  require "rspec/openhab/dsl/rules/rspec"
76
82
  require "rspec/openhab/items"
77
- require "rspec/openhab/state"
78
- require "rspec/openhab/timer"
79
- require "rspec/openhab/trigger"
80
- require "rspec/openhab/wait"
83
+ require "rspec/openhab/helpers"
84
+ require "rspec/openhab/hooks"
85
+ require "rspec/openhab/suspend_rules"
86
+
81
87
  RSpec.configure do |config|
82
88
  config.include OpenHAB::Core::EntityLookup
83
89
  end
84
90
 
85
- RSpec::OpenHAB::Items.populate_items_from_api(api)
91
+ RSpec::OpenHAB::SuspendRules.suspend_rules do
92
+ RSpec::OpenHAB::Items.populate_items_from_api(api)
93
+ end
86
94
 
87
95
  # make bundler/inline _not_ destroy the already existing load path
88
96
  module Bundler
@@ -96,7 +104,7 @@ OPENHAB_AUTOMATION_PATH = "#{org.openhab.core.OpenHAB.config_folder}/automation/
96
104
 
97
105
  Dir["#{OPENHAB_AUTOMATION_PATH}/*.rb"].each do |f|
98
106
  load f
99
- rescue Exception => e # rubocop:disable Lint/RescueException
107
+ rescue Exception => e
100
108
  warn "Failed loading #{f}: #{e.inspect}"
101
109
  warn e.backtrace
102
110
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.11
5
5
  platform: java
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-02 00:00:00.000000000 Z
11
+ date: 2022-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '3.3'
97
+ - !ruby/object:Gem::Dependency
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.9'
103
+ name: timecop
104
+ prerelease: false
105
+ type: :runtime
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
97
111
  - !ruby/object:Gem::Dependency
98
112
  requirement: !ruby/object:Gem::Requirement
99
113
  requirements:
@@ -182,12 +196,12 @@ files:
182
196
  - lib/rspec/openhab/core/script_handling.rb
183
197
  - lib/rspec/openhab/dsl/imports.rb
184
198
  - lib/rspec/openhab/dsl/rules/rspec.rb
199
+ - lib/rspec/openhab/dsl/timers/timer.rb
200
+ - lib/rspec/openhab/helpers.rb
201
+ - lib/rspec/openhab/hooks.rb
185
202
  - lib/rspec/openhab/items.rb
186
- - lib/rspec/openhab/state.rb
187
- - lib/rspec/openhab/timer.rb
188
- - lib/rspec/openhab/trigger.rb
203
+ - lib/rspec/openhab/suspend_rules.rb
189
204
  - lib/rspec/openhab/version.rb
190
- - lib/rspec/openhab/wait.rb
191
205
  - vendor/gems/jar-dependencies-1.0.0/lib/jar-dependencies.rb
192
206
  - vendor/gems/jar-dependencies-1.0.0/lib/jar_dependencies.rb
193
207
  - vendor/gems/jar-dependencies-1.0.0/lib/jar_install_post_install_hook.rb
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.configure do |config|
4
- config.before(:each) do
5
- ep = $ir.event_publisher
6
-
7
- # stash event publishers to avoid triggering any rules
8
- $ir.for_each do |_provider, item|
9
- item.event_publisher = nil
10
- end
11
-
12
- $ir.for_each do |_provider, item| # rubocop:disable Style/CombinableLoops
13
- item.state = NULL # don't use update, to avoid triggering any rules
14
- end
15
- ensure
16
- $ir.for_each do |_provider, item|
17
- item.event_publisher = ep
18
- end
19
- end
20
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.configure do |config|
4
- config.before(:each) do
5
- OpenHAB::DSL::Timers.timer_manager.cancel_all
6
- wait_for_background_tasks
7
- end
8
- config.after(:each) do
9
- OpenHAB::DSL::Timers.timer_manager.cancel_all
10
- wait_for_background_tasks
11
- end
12
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RSpec
4
- module OpenHAB
5
- module Trigger
6
- def trigger_rule(rule_name, event = nil)
7
- @rules ||= ::OpenHAB::DSL::Rules::Rule.script_rules.each_with_object({}) { |r, obj| obj[r.name] = r }
8
-
9
- @rules.fetch(rule_name).execute(nil, { "event" => event })
10
- end
11
- end
12
- end
13
- end
14
-
15
- RSpec.configure do |config|
16
- config.include RSpec::OpenHAB::Trigger
17
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RSpec
4
- module OpenHAB
5
- module Wait
6
- def wait_for_rules
7
- wait_for_background_tasks(timers: false)
8
- end
9
-
10
- def wait_for_timers
11
- wait_for_background_tasks(rules: false)
12
- end
13
-
14
- def wait_for_background_tasks(rules: true, timers: true)
15
- loop do
16
- sleep 0.1
17
- next if java.lang.Thread.all_stack_traces.any? do |(t, stack)|
18
- # this is just an estimate. I see 9 when it's parked waiting
19
- # for an event, but once it hits ruby it gets real big real quick
20
- min_frames = 15
21
-
22
- case t.name
23
- when /^OH-scheduler-/
24
- # timer thread; born and die for each timer
25
- if thread_running?(t) || stack.length > min_frames
26
- logger.debug "thread #{t.name} is running (#{stack.length})"
27
- stack.each do |frame|
28
- logger.trace " #{frame}"
29
- end
30
- next timers
31
- end
32
- when /^OH-rule-/
33
-
34
- if thread_running?(t) || stack.length > min_frames
35
- logger.debug "thread #{t.name} is running (#{stack.length})"
36
- stack.each do |frame|
37
- logger.trace " #{frame}"
38
- end
39
-
40
- next rules
41
- end
42
- when /^OH-(?:eventwatcher|eventexecutor)-/
43
- # an event is making its way through the system
44
- if thread_running?(t)
45
- logger.debug "thread #{t.name} is running"
46
- next rules
47
- end
48
- end
49
- end
50
-
51
- # no need to retry if there were no timers
52
- break unless timers && wait_for_next_timer
53
- end
54
- end
55
-
56
- private
57
-
58
- def thread_running?(thread)
59
- ![java.lang.Thread::State::WAITING,
60
- java.lang.Thread::State::TIMED_WAITING].include?(thread.state)
61
- end
62
-
63
- def wait_for_next_timer
64
- latest = ::OpenHAB::DSL::Timers.timer_manager.instance_variable_get(:@timers).min_by(&:execution_time)
65
- return false unless latest
66
-
67
- delta = (latest.execution_time.to_instant.to_epoch_milli - java.time.Instant.now.to_epoch_milli) / 1000.0
68
- # in the past? it's probably executing
69
- return true if delta.negative?
70
-
71
- logger.info("Waiting #{delta}s for next timer") if delta > 5
72
-
73
- sleep(delta)
74
- true
75
- end
76
- end
77
- end
78
- end
79
-
80
- RSpec.configure do |config|
81
- config.include RSpec::OpenHAB::Wait
82
- end