rspec-openhab-scripting 0.0.9-java → 0.0.12-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: ec85830015f39fcbcf027f869f5af4b215717b1c027deb47959428a8f156b652
4
- data.tar.gz: 22050257da398fa958a657e69c773f27e215e2d478bacb9d9e1400f84b0c6191
3
+ metadata.gz: 826f0aaf58b9c4d57282365b2022123b632614c972c4236e4cbaef54f7112277
4
+ data.tar.gz: 04d5dbd825e8136d99ab46a435a7d1e073b4399efc9df9e787d871ebf604b7dd
5
5
  SHA512:
6
- metadata.gz: d29f9f59280c73b33cb7caef1bbde2baf7b1931bbf9c8fdd8ccb51c3ec8ec13bf9efe51d6e13a410fadd9a53bf78928f22cdc25917ee37d64dc66906fc47bcfc
7
- data.tar.gz: b0132dec180bf8e9bf6fca0e8223c67e6fda04529f376f74a9b2405c0defd01bf1661fed63534a15ccf4392440a4faefe8936f4a42428af8de1be5c6778755d8
6
+ metadata.gz: 0f21ccb93cb88bdcd1dc469b68c7d85c9e0ecb8c087be90cda85973d0435bf222428bfdc87956f14bab26f6b546627a74dab16d00640860ff61d9b8c9aac6389
7
+ data.tar.gz: 6bf658c0b46e17faed956322bfe950af70b232ec80579252c921ba59649ea70cfe350532d0388a4fe499e63f74b9c78768235169bf66927d4d2be11788148356
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "faraday_middleware"
3
+ require "faraday"
4
4
 
5
5
  module OpenHAB
6
6
  class API
7
7
  def initialize(url)
8
8
  @faraday = Faraday.new(url) do |f|
9
- f.request :retry
10
9
  f.response :raise_error
11
10
  f.response :json
12
- f.adapter :net_http_persistent
13
11
  f.path_prefix = "/rest/"
14
12
  end
15
13
  end
@@ -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)
@@ -157,6 +278,8 @@ module OpenHAB
157
278
  OpenHAB::Core::OSGI.register_service("org.openhab.core.items.MetadataRegistry", mr)
158
279
  mr.managed_provider = mmp = org.openhab.core.internal.items.ManagedMetadataProviderImpl.new(ss)
159
280
  mr.add_provider(mmp)
281
+ gmp = org.openhab.core.model.item.internal.GenericMetadataProvider.new
282
+ mr.add_provider(gmp)
160
283
  ir = org.openhab.core.internal.items.ItemRegistryImpl.new(mr)
161
284
  ir.managed_provider = mip = org.openhab.core.items.ManagedItemProvider.new(ss, nil)
162
285
  ir.add_provider(mip)
@@ -171,6 +294,22 @@ module OpenHAB
171
294
  rr.managed_provider = mrp = org.openhab.core.automation.ManagedRuleProvider.new(ss)
172
295
  rr.add_provider(mrp)
173
296
  iclr = org.openhab.core.thing.link.ItemChannelLinkRegistry.new(tr, ir)
297
+ ctr = org.openhab.core.thing.type.ChannelTypeRegistry.new
298
+ ttr = org.openhab.core.thing.type.ThingTypeRegistry.new(ctr)
299
+
300
+ safe_emf = org.openhab.core.model.core.internal.SafeEMFImpl.new
301
+ model_repository = org.openhab.core.model.core.internal.ModelRepositoryImpl.new(safe_emf)
302
+
303
+ # set up state descriptions
304
+ sds = org.openhab.core.internal.service.StateDescriptionServiceImpl.new
305
+ gip = org.openhab.core.model.item.internal.GenericItemProvider.new(model_repository, gmp, {})
306
+ sds.add_state_description_fragment_provider(gip)
307
+ msdfp = org.openhab.core.internal.items.MetadataStateDescriptionFragmentProvider.new(mr, {})
308
+ sds.add_state_description_fragment_provider(msdfp)
309
+ csdp = org.openhab.core.thing.internal.ChannelStateDescriptionProvider.new(iclr, ttr, tr)
310
+ csdp.activate(org.osgi.framework.Constants::SERVICE_RANKING => java.lang.Integer.new(-1))
311
+ sds.add_state_description_fragment_provider(csdp)
312
+ ir.state_description_service = sds
174
313
 
175
314
  # set up stuff accessed from rules
176
315
  preset = org.openhab.core.automation.module.script.internal.defaultscope
@@ -216,8 +355,7 @@ module OpenHAB
216
355
  $se = $scriptExtension = sew
217
356
 
218
357
  # need to create some singletons referencing registries
219
- scheduler = org.openhab.core.internal.scheduler.SchedulerImpl.new
220
- 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)
221
359
  org.openhab.core.model.script.internal.engine.action.SemanticsActionService.new(ir)
222
360
 
223
361
  # link up event bus infrastructure
@@ -247,6 +385,12 @@ module OpenHAB
247
385
 
248
386
  rs = org.openhab.core.internal.service.ReadyServiceImpl.new
249
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
250
394
  re.add_module_handler_factory(cmhf)
251
395
  re.add_module_handler_factory(scmhf)
252
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 do
5
+ suspend_rules do
6
+ $ir.for_each do |_provider, item|
7
+ next if item.is_a?(GroupItem) # groups only have calculated states
8
+
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
@@ -8,25 +8,24 @@ module RSpec
8
8
  all_items = api.items
9
9
 
10
10
  gfh = org.openhab.core.internal.items.GroupFunctionHelper.new
11
+ item_factory = org.openhab.core.library.CoreItemFactory.new
11
12
 
12
13
  all_items.each do |item_json|
13
- type, _dimension = item_json["type"].split(":")
14
+ full_type = item_json["type"]
15
+ name = item_json["name"]
16
+
17
+ type, _dimension = full_type.split(":")
14
18
  if type == "Group"
15
- if item_json["groupType"]
16
- type, _dimension = item_json["groupType"].split(":")
17
- klass = ::OpenHAB::DSL::Items.const_get(:"#{type}Item")
18
- base_item = klass.new(item_json["name"])
19
- end
19
+ base_item = item_factory.create_item(item_json["groupType"], name) if item_json["groupType"]
20
20
  if item_json["function"]
21
21
  dto = org.openhab.core.items.dto.GroupFunctionDTO.new
22
22
  dto.name = item_json.dig("function", "name")
23
23
  dto.params = item_json.dig("function", "params")
24
24
  function = gfh.create_group_function(dto, base_item)
25
25
  end
26
- item = GroupItem.new(item_json["name"], base_item, function)
26
+ item = GroupItem.new(name, base_item, function)
27
27
  else
28
- klass = ::OpenHAB::DSL::Items.const_get(:"#{type}Item")
29
- item = klass.new(item_json["name"])
28
+ item = item_factory.create_item(full_type, name)
30
29
  end
31
30
 
32
31
  item.label = item_json["label"]
@@ -36,6 +35,8 @@ module RSpec
36
35
  item_json["metadata"]&.each do |key, config|
37
36
  item.meta[key] = config["value"], config["config"]
38
37
  end
38
+ item.meta["stateDescription"] = item_json["stateDescription"] if item_json["stateDescription"]
39
+ item.category = item_json["category"] if item_json["category"]
39
40
 
40
41
  $ir.add(item)
41
42
  end
@@ -48,30 +49,6 @@ module RSpec
48
49
  end
49
50
  end
50
51
  end
51
-
52
- def autoupdate_all_items
53
- @autoupdated_items ||= {}
54
- $ir.for_each do |_provider, item|
55
- @autoupdated_items[item] = item.meta.delete("autoupdate") if item.meta.key?("autoupdate")
56
- end
57
- end
58
-
59
- private
60
-
61
- def restore_autoupdate_items
62
- return unless instance_variable_defined?(:@autoupdated_items)
63
-
64
- @autoupdated_items.each do |(item, meta)|
65
- item.meta["autoupdate"] = meta
66
- end
67
- end
68
-
69
- ::RSpec.configure do |config|
70
- config.include(self)
71
- config.after do
72
- restore_autoupdate_items
73
- end
74
- end
75
52
  end
76
53
  end
77
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.9"
5
+ VERSION = "0.0.12"
6
6
  end
7
7
  end
@@ -40,6 +40,7 @@ maven_require do
40
40
  require "jar org.openhab.core.bundles, org.openhab.core.config.core, #{openhab_version}"
41
41
  require "jar org.openhab.core.bundles, org.openhab.core.io.monitor, #{openhab_version}"
42
42
  require "jar org.openhab.core.bundles, org.openhab.core.model.core, #{openhab_version}"
43
+ require "jar org.openhab.core.bundles, org.openhab.core.model.item, #{openhab_version}"
43
44
  require "jar org.openhab.core.bundles, org.openhab.core.model.script, #{openhab_version}"
44
45
  require "jar org.openhab.core.bundles, org.openhab.core.semantics, #{openhab_version}"
45
46
  require "jar org.openhab.core.bundles, org.openhab.core.thing, #{openhab_version}"
@@ -56,10 +57,13 @@ def oh.config_folder
56
57
  end
57
58
 
58
59
  # global variables need to be set up before openhab-scripting loads
60
+ require "timecop"
59
61
  require "openhab/log/logger"
60
62
  require "rspec/openhab/core/logger"
63
+
61
64
  # during testing, we don't want "regular" output from rules
62
65
  OpenHAB::Log.logger("org.openhab.automation.jruby.runtime").level = :warn
66
+ OpenHAB::Log.logger("org.openhab.automation.jruby.logger").level = :warn
63
67
  require "openhab/dsl/imports"
64
68
  OpenHAB::DSL::Imports.api = api
65
69
  OpenHAB::DSL::Imports.import_presets
@@ -69,19 +73,23 @@ require "openhab"
69
73
  require "rspec/openhab/actions"
70
74
  require "rspec/openhab/core/cron_scheduler"
71
75
 
76
+ # override several timer methods
77
+ require_relative "rspec/openhab/dsl/timers/timer"
78
+
72
79
  # RSpec additions
73
80
  require "rspec/core"
74
- require "rspec/openhab/dsl/rules/rspec"
75
81
  require "rspec/openhab/items"
76
- require "rspec/openhab/state"
77
- require "rspec/openhab/timer"
78
- require "rspec/openhab/trigger"
79
- require "rspec/openhab/wait"
82
+ require "rspec/openhab/helpers"
83
+ require "rspec/openhab/hooks"
84
+ require "rspec/openhab/suspend_rules"
85
+
80
86
  RSpec.configure do |config|
81
87
  config.include OpenHAB::Core::EntityLookup
82
88
  end
83
89
 
84
- RSpec::OpenHAB::Items.populate_items_from_api(api)
90
+ RSpec::OpenHAB::SuspendRules.suspend_rules do
91
+ RSpec::OpenHAB::Items.populate_items_from_api(api)
92
+ end
85
93
 
86
94
  # make bundler/inline _not_ destroy the already existing load path
87
95
  module Bundler
@@ -93,9 +101,11 @@ end
93
101
  # load rules files
94
102
  OPENHAB_AUTOMATION_PATH = "#{org.openhab.core.OpenHAB.config_folder}/automation/jsr223/ruby/personal"
95
103
 
96
- Dir["#{OPENHAB_AUTOMATION_PATH}/*.rb"].each do |f|
97
- load f
98
- rescue Exception => e # rubocop:disable Lint/RescueException
99
- warn "Failed loading #{f}: #{e.inspect}"
100
- warn e.backtrace
104
+ RSpec::OpenHAB::SuspendRules.suspend_rules do
105
+ Dir["#{OPENHAB_AUTOMATION_PATH}/*.rb"].each do |f|
106
+ load f
107
+ rescue Exception => e
108
+ warn "Failed loading #{f}: #{e.inspect}"
109
+ warn e.backtrace
110
+ end
101
111
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-openhab-scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.12
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
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '1.1'
19
- name: faraday_middleware
18
+ version: '2.3'
19
+ name: faraday
20
20
  prerelease: false
21
21
  type: :runtime
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '2.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
@@ -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:
@@ -181,13 +195,12 @@ files:
181
195
  - lib/rspec/openhab/core/osgi.rb
182
196
  - lib/rspec/openhab/core/script_handling.rb
183
197
  - lib/rspec/openhab/dsl/imports.rb
184
- - lib/rspec/openhab/dsl/rules/rspec.rb
198
+ - lib/rspec/openhab/dsl/timers/timer.rb
199
+ - lib/rspec/openhab/helpers.rb
200
+ - lib/rspec/openhab/hooks.rb
185
201
  - lib/rspec/openhab/items.rb
186
- - lib/rspec/openhab/state.rb
187
- - lib/rspec/openhab/timer.rb
188
- - lib/rspec/openhab/trigger.rb
202
+ - lib/rspec/openhab/suspend_rules.rb
189
203
  - lib/rspec/openhab/version.rb
190
- - lib/rspec/openhab/wait.rb
191
204
  - vendor/gems/jar-dependencies-1.0.0/lib/jar-dependencies.rb
192
205
  - vendor/gems/jar-dependencies-1.0.0/lib/jar_dependencies.rb
193
206
  - vendor/gems/jar-dependencies-1.0.0/lib/jar_install_post_install_hook.rb
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OpenHAB
4
- module DSL
5
- module Rules
6
- class RuleConfig
7
- # override on_start to never work
8
- def on_start?
9
- false
10
- end
11
- end
12
- end
13
- end
14
- end
@@ -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