rspec-openhab-scripting 0.0.8-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 +4 -4
- data/lib/rspec/openhab/dsl/imports.rb +154 -10
- data/lib/rspec/openhab/dsl/timers/timer.rb +85 -0
- data/lib/rspec/openhab/helpers.rb +42 -0
- data/lib/rspec/openhab/hooks.rb +18 -0
- data/lib/rspec/openhab/items.rb +10 -33
- data/lib/rspec/openhab/suspend_rules.rb +56 -0
- data/lib/rspec/openhab/version.rb +1 -1
- data/lib/rspec-openhab-scripting.rb +20 -6
- metadata +20 -6
- data/lib/rspec/openhab/state.rb +0 -20
- data/lib/rspec/openhab/timer.rb +0 -12
- data/lib/rspec/openhab/trigger.rb +0 -17
- data/lib/rspec/openhab/wait.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ef6a42e87b4466cf53972b65523d3a810060cbeac84497796ab4514b377d445
|
4
|
+
data.tar.gz: 0b3be611dddf1a4e5643cdb48fd3f8a05d35eda32e50e9b4062a063f49f6ad17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
137
|
-
|
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 =
|
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
|
-
|
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(: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
|
data/lib/rspec/openhab/items.rb
CHANGED
@@ -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
|
-
|
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(
|
26
|
+
item = GroupItem.new(name, base_item, function)
|
27
27
|
else
|
28
|
-
|
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
|
@@ -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,8 +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
|
+
|
64
|
+
# during testing, we don't want "regular" output from rules
|
65
|
+
OpenHAB::Log.logger("org.openhab.automation.jruby.runtime").level = :warn
|
66
|
+
OpenHAB::Log.logger("org.openhab.automation.jruby.logger").level = :warn
|
61
67
|
require "openhab/dsl/imports"
|
62
68
|
OpenHAB::DSL::Imports.api = api
|
63
69
|
OpenHAB::DSL::Imports.import_presets
|
@@ -67,16 +73,24 @@ require "openhab"
|
|
67
73
|
require "rspec/openhab/actions"
|
68
74
|
require "rspec/openhab/core/cron_scheduler"
|
69
75
|
|
76
|
+
# override several timer methods
|
77
|
+
require_relative "rspec/openhab/dsl/timers/timer"
|
78
|
+
|
70
79
|
# RSpec additions
|
71
80
|
require "rspec/core"
|
72
81
|
require "rspec/openhab/dsl/rules/rspec"
|
73
82
|
require "rspec/openhab/items"
|
74
|
-
require "rspec/openhab/
|
75
|
-
require "rspec/openhab/
|
76
|
-
require "rspec/openhab/
|
77
|
-
require "rspec/openhab/wait"
|
83
|
+
require "rspec/openhab/helpers"
|
84
|
+
require "rspec/openhab/hooks"
|
85
|
+
require "rspec/openhab/suspend_rules"
|
78
86
|
|
79
|
-
RSpec
|
87
|
+
RSpec.configure do |config|
|
88
|
+
config.include OpenHAB::Core::EntityLookup
|
89
|
+
end
|
90
|
+
|
91
|
+
RSpec::OpenHAB::SuspendRules.suspend_rules do
|
92
|
+
RSpec::OpenHAB::Items.populate_items_from_api(api)
|
93
|
+
end
|
80
94
|
|
81
95
|
# make bundler/inline _not_ destroy the already existing load path
|
82
96
|
module Bundler
|
@@ -90,7 +104,7 @@ OPENHAB_AUTOMATION_PATH = "#{org.openhab.core.OpenHAB.config_folder}/automation/
|
|
90
104
|
|
91
105
|
Dir["#{OPENHAB_AUTOMATION_PATH}/*.rb"].each do |f|
|
92
106
|
load f
|
93
|
-
rescue Exception => e
|
107
|
+
rescue Exception => e
|
94
108
|
warn "Failed loading #{f}: #{e.inspect}"
|
95
109
|
warn e.backtrace
|
96
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.
|
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-
|
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/
|
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
|
data/lib/rspec/openhab/state.rb
DELETED
@@ -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
|
data/lib/rspec/openhab/timer.rb
DELETED
@@ -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
|
data/lib/rspec/openhab/wait.rb
DELETED
@@ -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
|