openhab-jrubyscripting 5.0.0.rc1

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.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/lib/openhab/core/actions.rb +163 -0
  3. data/lib/openhab/core/entity_lookup.rb +144 -0
  4. data/lib/openhab/core/events/abstract_event.rb +17 -0
  5. data/lib/openhab/core/events/item_channel_link.rb +36 -0
  6. data/lib/openhab/core/events/item_command_event.rb +78 -0
  7. data/lib/openhab/core/events/item_event.rb +22 -0
  8. data/lib/openhab/core/events/item_state_changed_event.rb +52 -0
  9. data/lib/openhab/core/events/item_state_event.rb +51 -0
  10. data/lib/openhab/core/events/thing.rb +29 -0
  11. data/lib/openhab/core/events/thing_status_info_event.rb +53 -0
  12. data/lib/openhab/core/events.rb +10 -0
  13. data/lib/openhab/core/items/accepted_data_types.rb +29 -0
  14. data/lib/openhab/core/items/color_item.rb +52 -0
  15. data/lib/openhab/core/items/contact_item.rb +52 -0
  16. data/lib/openhab/core/items/date_time_item.rb +58 -0
  17. data/lib/openhab/core/items/dimmer_item.rb +148 -0
  18. data/lib/openhab/core/items/generic_item.rb +344 -0
  19. data/lib/openhab/core/items/group_item.rb +174 -0
  20. data/lib/openhab/core/items/image_item.rb +109 -0
  21. data/lib/openhab/core/items/location_item.rb +34 -0
  22. data/lib/openhab/core/items/metadata/hash.rb +390 -0
  23. data/lib/openhab/core/items/metadata/namespace_hash.rb +469 -0
  24. data/lib/openhab/core/items/metadata.rb +11 -0
  25. data/lib/openhab/core/items/number_item.rb +62 -0
  26. data/lib/openhab/core/items/numeric_item.rb +22 -0
  27. data/lib/openhab/core/items/persistence.rb +327 -0
  28. data/lib/openhab/core/items/player_item.rb +66 -0
  29. data/lib/openhab/core/items/proxy.rb +59 -0
  30. data/lib/openhab/core/items/registry.rb +66 -0
  31. data/lib/openhab/core/items/rollershutter_item.rb +68 -0
  32. data/lib/openhab/core/items/semantics/enumerable.rb +152 -0
  33. data/lib/openhab/core/items/semantics.rb +476 -0
  34. data/lib/openhab/core/items/state_storage.rb +53 -0
  35. data/lib/openhab/core/items/string_item.rb +28 -0
  36. data/lib/openhab/core/items/switch_item.rb +78 -0
  37. data/lib/openhab/core/items.rb +114 -0
  38. data/lib/openhab/core/lazy_array.rb +52 -0
  39. data/lib/openhab/core/profile_factory.rb +118 -0
  40. data/lib/openhab/core/script_handling.rb +55 -0
  41. data/lib/openhab/core/things/channel.rb +48 -0
  42. data/lib/openhab/core/things/channel_uid.rb +51 -0
  43. data/lib/openhab/core/things/item_channel_link.rb +33 -0
  44. data/lib/openhab/core/things/profile_callback.rb +52 -0
  45. data/lib/openhab/core/things/proxy.rb +69 -0
  46. data/lib/openhab/core/things/registry.rb +46 -0
  47. data/lib/openhab/core/things/thing.rb +194 -0
  48. data/lib/openhab/core/things.rb +22 -0
  49. data/lib/openhab/core/timer.rb +128 -0
  50. data/lib/openhab/core/types/comparable_type.rb +23 -0
  51. data/lib/openhab/core/types/date_time_type.rb +259 -0
  52. data/lib/openhab/core/types/decimal_type.rb +192 -0
  53. data/lib/openhab/core/types/hsb_type.rb +183 -0
  54. data/lib/openhab/core/types/increase_decrease_type.rb +34 -0
  55. data/lib/openhab/core/types/next_previous_type.rb +34 -0
  56. data/lib/openhab/core/types/numeric_type.rb +52 -0
  57. data/lib/openhab/core/types/on_off_type.rb +46 -0
  58. data/lib/openhab/core/types/open_closed_type.rb +41 -0
  59. data/lib/openhab/core/types/percent_type.rb +95 -0
  60. data/lib/openhab/core/types/play_pause_type.rb +38 -0
  61. data/lib/openhab/core/types/point_type.rb +117 -0
  62. data/lib/openhab/core/types/quantity_type.rb +327 -0
  63. data/lib/openhab/core/types/raw_type.rb +26 -0
  64. data/lib/openhab/core/types/refresh_type.rb +27 -0
  65. data/lib/openhab/core/types/rewind_fastforward_type.rb +38 -0
  66. data/lib/openhab/core/types/stop_move_type.rb +34 -0
  67. data/lib/openhab/core/types/string_type.rb +76 -0
  68. data/lib/openhab/core/types/type.rb +117 -0
  69. data/lib/openhab/core/types/un_def_type.rb +38 -0
  70. data/lib/openhab/core/types/up_down_type.rb +50 -0
  71. data/lib/openhab/core/types.rb +69 -0
  72. data/lib/openhab/core/uid.rb +36 -0
  73. data/lib/openhab/core.rb +85 -0
  74. data/lib/openhab/core_ext/java/duration.rb +115 -0
  75. data/lib/openhab/core_ext/java/local_date.rb +93 -0
  76. data/lib/openhab/core_ext/java/local_time.rb +106 -0
  77. data/lib/openhab/core_ext/java/month.rb +59 -0
  78. data/lib/openhab/core_ext/java/month_day.rb +105 -0
  79. data/lib/openhab/core_ext/java/period.rb +103 -0
  80. data/lib/openhab/core_ext/java/temporal_amount.rb +34 -0
  81. data/lib/openhab/core_ext/java/time.rb +58 -0
  82. data/lib/openhab/core_ext/java/unit.rb +15 -0
  83. data/lib/openhab/core_ext/java/zoned_date_time.rb +116 -0
  84. data/lib/openhab/core_ext/ruby/array.rb +21 -0
  85. data/lib/openhab/core_ext/ruby/class.rb +15 -0
  86. data/lib/openhab/core_ext/ruby/date.rb +89 -0
  87. data/lib/openhab/core_ext/ruby/numeric.rb +190 -0
  88. data/lib/openhab/core_ext/ruby/range.rb +70 -0
  89. data/lib/openhab/core_ext/ruby/time.rb +104 -0
  90. data/lib/openhab/core_ext.rb +18 -0
  91. data/lib/openhab/dsl/events/watch_event.rb +18 -0
  92. data/lib/openhab/dsl/events.rb +9 -0
  93. data/lib/openhab/dsl/gems.rb +3 -0
  94. data/lib/openhab/dsl/items/builder.rb +618 -0
  95. data/lib/openhab/dsl/items/ensure.rb +93 -0
  96. data/lib/openhab/dsl/items/timed_command.rb +236 -0
  97. data/lib/openhab/dsl/rules/automation_rule.rb +308 -0
  98. data/lib/openhab/dsl/rules/builder.rb +1373 -0
  99. data/lib/openhab/dsl/rules/guard.rb +115 -0
  100. data/lib/openhab/dsl/rules/name_inference.rb +160 -0
  101. data/lib/openhab/dsl/rules/property.rb +76 -0
  102. data/lib/openhab/dsl/rules/rule_triggers.rb +96 -0
  103. data/lib/openhab/dsl/rules/terse.rb +63 -0
  104. data/lib/openhab/dsl/rules/triggers/changed.rb +169 -0
  105. data/lib/openhab/dsl/rules/triggers/channel.rb +57 -0
  106. data/lib/openhab/dsl/rules/triggers/command.rb +107 -0
  107. data/lib/openhab/dsl/rules/triggers/conditions/duration.rb +161 -0
  108. data/lib/openhab/dsl/rules/triggers/conditions/proc.rb +164 -0
  109. data/lib/openhab/dsl/rules/triggers/cron/cron.rb +195 -0
  110. data/lib/openhab/dsl/rules/triggers/cron/cron_handler.rb +127 -0
  111. data/lib/openhab/dsl/rules/triggers/trigger.rb +56 -0
  112. data/lib/openhab/dsl/rules/triggers/updated.rb +130 -0
  113. data/lib/openhab/dsl/rules/triggers/watch/watch.rb +55 -0
  114. data/lib/openhab/dsl/rules/triggers/watch/watch_handler.rb +155 -0
  115. data/lib/openhab/dsl/rules/triggers.rb +12 -0
  116. data/lib/openhab/dsl/rules.rb +29 -0
  117. data/lib/openhab/dsl/script_handling.rb +55 -0
  118. data/lib/openhab/dsl/things/builder.rb +263 -0
  119. data/lib/openhab/dsl/thread_local.rb +48 -0
  120. data/lib/openhab/dsl/timer_manager.rb +191 -0
  121. data/lib/openhab/dsl/version.rb +9 -0
  122. data/lib/openhab/dsl.rb +686 -0
  123. data/lib/openhab/log.rb +348 -0
  124. data/lib/openhab/osgi.rb +70 -0
  125. data/lib/openhab/rspec/configuration.rb +56 -0
  126. data/lib/openhab/rspec/example_group.rb +90 -0
  127. data/lib/openhab/rspec/helpers.rb +439 -0
  128. data/lib/openhab/rspec/hooks.rb +93 -0
  129. data/lib/openhab/rspec/jruby.rb +46 -0
  130. data/lib/openhab/rspec/karaf.rb +811 -0
  131. data/lib/openhab/rspec/mocks/bundle_install_support.rb +25 -0
  132. data/lib/openhab/rspec/mocks/bundle_resolver.rb +30 -0
  133. data/lib/openhab/rspec/mocks/event_admin.rb +146 -0
  134. data/lib/openhab/rspec/mocks/metadata_provider.rb +75 -0
  135. data/lib/openhab/rspec/mocks/persistence_service.rb +140 -0
  136. data/lib/openhab/rspec/mocks/safe_caller.rb +40 -0
  137. data/lib/openhab/rspec/mocks/synchronous_executor.rb +56 -0
  138. data/lib/openhab/rspec/mocks/thing_handler.rb +76 -0
  139. data/lib/openhab/rspec/mocks/timer.rb +95 -0
  140. data/lib/openhab/rspec/openhab/core/actions.rb +26 -0
  141. data/lib/openhab/rspec/openhab/core/items/proxy.rb +27 -0
  142. data/lib/openhab/rspec/openhab/core/things/proxy.rb +27 -0
  143. data/lib/openhab/rspec/shell.rb +31 -0
  144. data/lib/openhab/rspec/suspend_rules.rb +60 -0
  145. data/lib/openhab/rspec.rb +17 -0
  146. data/lib/openhab/yard/cli/stats.rb +23 -0
  147. data/lib/openhab/yard/code_objects/group_object.rb +17 -0
  148. data/lib/openhab/yard/code_objects/java/base.rb +31 -0
  149. data/lib/openhab/yard/code_objects/java/class_object.rb +11 -0
  150. data/lib/openhab/yard/code_objects/java/field_object.rb +15 -0
  151. data/lib/openhab/yard/code_objects/java/interface_object.rb +15 -0
  152. data/lib/openhab/yard/code_objects/java/package_object.rb +11 -0
  153. data/lib/openhab/yard/code_objects/java/proxy.rb +23 -0
  154. data/lib/openhab/yard/handlers/jruby/base.rb +49 -0
  155. data/lib/openhab/yard/handlers/jruby/class_handler.rb +18 -0
  156. data/lib/openhab/yard/handlers/jruby/constant_handler.rb +18 -0
  157. data/lib/openhab/yard/handlers/jruby/java_import_handler.rb +27 -0
  158. data/lib/openhab/yard/handlers/jruby/mixin_handler.rb +23 -0
  159. data/lib/openhab/yard/html_helper.rb +44 -0
  160. data/lib/openhab/yard/tags/constant_directive.rb +20 -0
  161. data/lib/openhab/yard/tags/group_directive.rb +24 -0
  162. data/lib/openhab/yard/tags/library.rb +3 -0
  163. data/lib/openhab/yard.rb +32 -0
  164. metadata +504 -0
@@ -0,0 +1,439 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ #
5
+ # Contains loaded Ruby transformations as class methods
6
+ #
7
+ # Only during testing.
8
+ #
9
+ # @example Corresponds to `transform/compass.script`
10
+ # OpenHAB::Transform.compass("59 °")
11
+ #
12
+ # @example Corresponds to `transform/compass.script`
13
+ # OpenHAB::Transform.compass("30", param: "7")
14
+ #
15
+ # @example Corresponds to `transform/ruby/compass.script`
16
+ # OpenHAB::Transform::Ruby.compass("59 °")
17
+
18
+ module Transform
19
+ class << self
20
+ # @!visibility private
21
+ def add_script(modules, script)
22
+ full_name = modules.join("/")
23
+ name = modules.pop
24
+ (@scripts ||= {})[full_name] = engine_factory.script_engine.compile(script)
25
+
26
+ mod = modules.inject(self) { |m, n| m.const_get(n, false) }
27
+ mod.singleton_class.define_method(name) do |input, **kwargs|
28
+ Transform.send(:transform, full_name, input, kwargs)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def engine_factory
35
+ @engine_factory ||= org.jruby.embed.jsr223.JRubyEngineFactory.new
36
+ end
37
+
38
+ def transform(name, input, kwargs)
39
+ script = @scripts[name]
40
+ ctx = script.engine.context
41
+ ctx.set_attribute("input", input.to_s, javax.script.ScriptContext::ENGINE_SCOPE)
42
+ kwargs.each do |(k, v)|
43
+ ctx.set_attribute(k.to_s, v.to_s, javax.script.ScriptContext::ENGINE_SCOPE)
44
+ end
45
+ script.eval
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ module OpenHAB
52
+ module RSpec
53
+ #
54
+ # Provides helper methods for use in specs, to easily work with and adjust
55
+ # the OpenHAB environment.
56
+ #
57
+ # These methods are automatically available in RSpec spec blocks, as well
58
+ # as other per-spec hooks like `before` and `after`. You can also call them
59
+ # explicitly.
60
+ #
61
+ module Helpers
62
+ module BindingHelper
63
+ # @!visibility private
64
+ def add_kwargs_to_current_binding(binding, kwargs)
65
+ kwargs.each { |(k, v)| binding.local_variable_set(k, v) }
66
+ end
67
+ end
68
+ private_constant :BindingHelper
69
+
70
+ # Yard crashes on this; be tricky so it doesn't realize what's going on
71
+ s = singleton_class
72
+ s.include(Helpers)
73
+
74
+ module_function
75
+
76
+ #
77
+ # Reconfigure all items to autoupdate
78
+ #
79
+ # To bypass any items configured to not autoupdate, waiting for the binding to update them.
80
+ #
81
+ # @return [void]
82
+ #
83
+ def autoupdate_all_items
84
+ if instance_variable_defined?(:@autoupdated_items)
85
+ raise RuntimeError "You should only call `autoupdate_all_items` once per spec"
86
+ end
87
+
88
+ @autoupdated_items = []
89
+
90
+ $ir.for_each do |_provider, item|
91
+ if (hash = item.metadata.delete("autoupdate"))
92
+ @autoupdated_items << hash
93
+ item.metadata["autoupdate"] = "true"
94
+ end
95
+ end
96
+ end
97
+
98
+ #
99
+ # Execute all pending timers
100
+ #
101
+ # @return [void]
102
+ #
103
+ def execute_timers
104
+ raise "Cannot execute timers when timers aren't mocked" unless self.class.mock_timers?
105
+
106
+ now = ZonedDateTime.now
107
+ DSL::TimerManager.instance.instance_variable_get(:@timers).each_key do |t|
108
+ t.execute if t.active? && t.execution_time <= now
109
+ end
110
+ end
111
+
112
+ #
113
+ # Wait `duration` seconds, then execute any pending timers
114
+ #
115
+ # If timers are mocked, it will use Timecop. If they're not mocked, it
116
+ # will just sleep for `duration`
117
+ #
118
+ # @return [void]
119
+ #
120
+ def time_travel_and_execute_timers(duration)
121
+ if self.class.mock_timers?
122
+ Timecop.travel(duration)
123
+ execute_timers
124
+ else
125
+ sleep duration
126
+ end
127
+ end
128
+
129
+ #
130
+ # Suspend rules for the duration of the block
131
+ #
132
+ # @return [Object] The return value from the block.
133
+ #
134
+ def suspend_rules(&block)
135
+ SuspendRules.suspend_rules(&block)
136
+ end
137
+
138
+ #
139
+ # Calls the block repeatedly until the expectations inside pass.
140
+ #
141
+ # @param [Duration] how_long how long to keep trying before giving up
142
+ # @yield
143
+ # @return [void]
144
+ def wait(how_long = 2.seconds)
145
+ now = Time.now
146
+
147
+ begin
148
+ yield
149
+ rescue ::RSpec::Expectations::ExpectationNotMetError,
150
+ ::RSpec::Mocks::MockExpectationError
151
+ raise if Time.now > now + how_long
152
+
153
+ sleep 0.1
154
+ retry
155
+ end
156
+ end
157
+
158
+ #
159
+ # Manually send an event to a trigger channel
160
+ #
161
+ # @param [String, Core::Things::Channel, Core::Things::ChannelUID] channel The channel to trigger.
162
+ # @param [String] event The event data to send to the channel.
163
+ # @return [void]
164
+ #
165
+ def trigger_channel(channel, event = "")
166
+ channel = org.openhab.core.thing.ChannelUID.new(channel) if channel.is_a?(String)
167
+ channel = channel.uid if channel.is_a?(org.openhab.core.thing.Channel)
168
+ thing = channel.thing
169
+ thing.handler.callback.channel_triggered(nil, channel, event)
170
+ end
171
+
172
+ #
173
+ # Require all files configured to be autorequired with the jrubyscripting addon in OpenHAB.
174
+ #
175
+ # This method is normally called by RSpec hooks.
176
+ #
177
+ # @return [void]
178
+ #
179
+ def autorequires
180
+ requires = jrubyscripting_config&.get("require") || ""
181
+ requires.split(",").each do |f|
182
+ require f.strip
183
+ end
184
+ end
185
+
186
+ #
187
+ # Launch the karaf instance
188
+ #
189
+ # This method is normally called by RSpec hooks.
190
+ #
191
+ # @return [void]
192
+ # @see Configuration
193
+ #
194
+ def launch_karaf(include_bindings: true,
195
+ include_jsondb: true,
196
+ private_confdir: false,
197
+ use_root_instance: false)
198
+ karaf = Karaf.new("#{Dir.pwd}/.karaf")
199
+ karaf.include_bindings = include_bindings
200
+ karaf.include_jsondb = include_jsondb
201
+ karaf.private_confdir = private_confdir
202
+ karaf.use_root_instance = use_root_instance
203
+ main = karaf.launch
204
+
205
+ ENV["RUBYLIB"] ||= ""
206
+ ENV["RUBYLIB"] += ":" unless ENV["RUBYLIB"].empty?
207
+ ENV["RUBYLIB"] += rubylib_dir
208
+ require "openhab/dsl"
209
+
210
+ require_relative "mocks/persistence_service"
211
+ require_relative "mocks/metadata_provider"
212
+ require_relative "mocks/timer"
213
+
214
+ # override several DSL methods
215
+ require_relative "openhab/core/items/proxy"
216
+ require_relative "openhab/core/things/proxy"
217
+ require_relative "openhab/core/actions"
218
+
219
+ ps = Mocks::PersistenceService.instance
220
+ bundle = org.osgi.framework.FrameworkUtil.get_bundle(org.openhab.core.persistence.PersistenceService)
221
+ bundle.bundle_context.register_service(org.openhab.core.persistence.PersistenceService.java_class, ps, nil)
222
+
223
+ # wait for the rule engine
224
+ rs = OSGi.service("org.openhab.core.service.ReadyService")
225
+ filter = org.openhab.core.service.ReadyMarkerFilter.new
226
+ .with_type(org.openhab.core.service.StartLevelService::STARTLEVEL_MARKER_TYPE)
227
+ .with_identifier(org.openhab.core.service.StartLevelService::STARTLEVEL_RULEENGINE.to_s)
228
+
229
+ karaf.send(:wait) do |continue|
230
+ rs.register_tracker(org.openhab.core.service.ReadyService::ReadyTracker.impl { continue.call }, filter)
231
+ end
232
+
233
+ # RSpec additions
234
+ require_relative "suspend_rules"
235
+
236
+ if defined?(::RSpec)
237
+ ::RSpec.configure do |config|
238
+ config.include OpenHAB::DSL
239
+ end
240
+ end
241
+ main
242
+ rescue Exception => e
243
+ puts e.inspect
244
+ puts e.backtrace
245
+ raise
246
+ end
247
+
248
+ #
249
+ # Load all Ruby rules in the config/automation directory
250
+ #
251
+ # This method is normally called by RSpec hooks.
252
+ #
253
+ # @return [void]
254
+ #
255
+ def load_rules
256
+ automation_path = "#{org.openhab.core.OpenHAB.config_folder}/automation/jsr223"
257
+
258
+ SuspendRules.suspend_rules do
259
+ files = Dir["#{automation_path}/**/*.rb"]
260
+ files.sort_by { |f| [get_start_level(f), f] }.each do |f|
261
+ load f
262
+ rescue Exception => e
263
+ warn "Failed loading #{f}: #{e.inspect}"
264
+ warn e.backtrace
265
+ end
266
+ end
267
+ end
268
+
269
+ #
270
+ # Load all Ruby transformations in the config/transform directory
271
+ #
272
+ # Since Ruby transformations must end with the .script extension, you must include
273
+ # an Emacs modeline comment (`# -*- mode: ruby -*-`) in your script for it to be
274
+ # recognized.
275
+ #
276
+ # This method is normally called by RSpec hooks.
277
+ #
278
+ # @return [void]
279
+ #
280
+ def load_transforms
281
+ transform_path = "#{org.openhab.core.OpenHAB.config_folder}/transform"
282
+ Dir["#{transform_path}/**/*.script"].each do |filename|
283
+ script = File.read(filename)
284
+ next unless ruby_file?(script)
285
+
286
+ filename.slice!(0..transform_path.length)
287
+ dir = File.dirname(filename)
288
+ modules = (dir == ".") ? [] : moduleize(dir)
289
+ basename = File.basename(filename)
290
+ method = basename[0...-7]
291
+ modules << method
292
+ Transform.add_script(modules, script)
293
+ end
294
+ end
295
+
296
+ #
297
+ # Install an OpenHAB addon
298
+ #
299
+ # @param [String] addon_id The addon id, such as "binding-mqtt"
300
+ # @param [true,false] wait Wait until OSGi has confirmed the bundle is installed and running before returning.
301
+ # @param [String,Array<String>] ready_markers Array of ready marker types to wait for.
302
+ # The addon's bundle id is used as the identifier.
303
+ # @return [void]
304
+ #
305
+ def install_addon(addon_id, wait: true, ready_markers: nil)
306
+ addon_service = OSGi.service("org.openhab.core.addon.AddonService")
307
+ addon_service.install(addon_id)
308
+ return unless wait
309
+
310
+ addon = nil
311
+ loop do
312
+ addon = addon_service.get_addon(addon_id, nil)
313
+ break if addon.installed?
314
+
315
+ sleep 0.25
316
+ end
317
+
318
+ return unless ready_markers
319
+
320
+ package_id = addon.logger_packages.first
321
+
322
+ ready_markers = Array(ready_markers).map do |marker|
323
+ case marker
324
+ when String
325
+ org.openhab.core.service.ReadyMarker.new(marker, package_id)
326
+ else
327
+ marker
328
+ end
329
+ end
330
+
331
+ rs = OSGi.service("org.openhab.core.service.ReadyService")
332
+ loop do
333
+ break if ready_markers.all? { |rm| rs.ready?(rm) }
334
+
335
+ sleep 0.25
336
+ end
337
+ end
338
+
339
+ # @return [String] The filename of the OpenHAB log.
340
+ def log_file
341
+ "#{java.lang.System.get_property("openhab.logdir", nil)}/openhab.log"
342
+ end
343
+
344
+ #
345
+ # @return [Array<String>] The log lines since this spec started.
346
+ #
347
+ # @example
348
+ # it "logs" do
349
+ # logger.trace("log line")
350
+ # expect(spec_log_lines).to include(match(/TRACE.*log line/))
351
+ # end
352
+ #
353
+ def spec_log_lines
354
+ File.open(log_file, "rb") do |f|
355
+ f.seek(@log_index) if @log_index
356
+ f.read.split("\n")
357
+ end
358
+ end
359
+
360
+ private
361
+
362
+ def jrubyscripting_config
363
+ ca = OSGi.service("org.osgi.service.cm.ConfigurationAdmin")
364
+ ca.get_configuration("org.openhab.automation.jrubyscripting", nil)&.properties
365
+ end
366
+
367
+ def rubylib_dir
368
+ jrubyscripting_config&.get("rubylib") || "#{org.openhab.core.OpenHAB.config_folder}/automation/lib/ruby"
369
+ end
370
+
371
+ def get_start_level(file)
372
+ return ($1 || $2).to_i if file =~ %r{/sl(\d{2})/[^/]+$|\.sl(\d{2})\.[^/.]+$}
373
+
374
+ 50
375
+ end
376
+
377
+ EMACS_MODELINE_REGEXP = /# -\*-(.+)-\*-/.freeze
378
+ private_constant :EMACS_MODELINE_REGEXP
379
+
380
+ def parse_emacs_modeline(line)
381
+ line[EMACS_MODELINE_REGEXP, 1]
382
+ &.split(";")
383
+ &.map(&:strip)
384
+ &.map { |l| l.split(":", 2).map(&:strip).tap { |a| a[1] ||= nil } }
385
+ &.to_h
386
+ end
387
+
388
+ def ruby_file?(script)
389
+ # check the first 1KB for an emacs magic comment
390
+ script[0..1024].split("\n").any? { |line| parse_emacs_modeline(line)&.dig("mode") == "ruby" }
391
+ end
392
+
393
+ def moduleize(term)
394
+ term
395
+ .sub(/^[a-z\d]*/, &:capitalize)
396
+ .gsub(%r{(?:_|(/))([a-z\d]*)}) { "#{$1}#{$2.capitalize}" }
397
+ .split("/")
398
+ end
399
+
400
+ # need to transfer autoupdate metadata from GenericMetadataProvider to ManagedMetadataProvider
401
+ # so that we can mutate it in the future
402
+ def set_up_autoupdates
403
+ gmp = OSGi.service("org.openhab.core.model.item.internal.GenericMetadataProvider")
404
+ mr = Core::Items::Metadata::NamespaceHash.registry
405
+ mmp = mr.managed_provider.get
406
+ to_add = []
407
+ gmp.all.each do |metadata|
408
+ next unless metadata.uid.namespace == "autoupdate"
409
+
410
+ to_add << metadata
411
+ end
412
+ gmp.remove_metadata_by_namespace("autoupdate")
413
+
414
+ to_add.each do |m|
415
+ # we can't just update; we need to remove and add
416
+ # because this was a duplicate key, so the ManagedProvider
417
+ # knows about it, but the registry does not. So
418
+ # removing and adding gets it to notify the registry
419
+ # that it has it
420
+ mmp.remove(m.uid) if mmp.get(m.uid)
421
+ mmp.add(m)
422
+ end
423
+ end
424
+
425
+ def restore_autoupdate_items
426
+ return unless instance_variable_defined?(:@autoupdated_items)
427
+
428
+ @autoupdated_items&.each(&:commit)
429
+ @autoupdated_items = nil
430
+ end
431
+ end
432
+
433
+ if defined?(::RSpec)
434
+ ::RSpec.configure do |config|
435
+ config.include Helpers
436
+ end
437
+ end
438
+ end
439
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ #
5
+ # This module contains helper methods, hooks, and infrastracture to
6
+ # boot OpenHAB inside of JRuby, and run RSpec (or other Ruby processes)
7
+ # in that context.
8
+ #
9
+ # @see file:testing.md Testing Your Rules
10
+ #
11
+ module RSpec
12
+ Object.include Helpers if defined?(IRB)
13
+
14
+ Helpers.launch_karaf(
15
+ include_bindings: Configuration.include_bindings,
16
+ include_jsondb: Configuration.include_jsondb,
17
+ private_confdir: Configuration.private_confdir,
18
+ use_root_instance: Configuration.use_root_instance
19
+ )
20
+
21
+ if defined?(::RSpec)
22
+ ::RSpec.configure do |config|
23
+ require_relative "example_group"
24
+ config.include ExampleGroup
25
+
26
+ config.before(:suite) do
27
+ Helpers.autorequires unless Configuration.private_confdir
28
+ Helpers.send(:set_up_autoupdates)
29
+ Helpers.load_transforms
30
+ Helpers.load_rules
31
+ end
32
+
33
+ config.before do
34
+ suspend_rules do
35
+ $ir.for_each do |_provider, item|
36
+ next if item.is_a?(GroupItem) # groups only have calculated states
37
+
38
+ item.state = NULL unless item.raw_state == NULL
39
+ end
40
+ end
41
+ @known_rules = Core.rule_registry.all.map(&:uid)
42
+ end
43
+
44
+ config.before do |example|
45
+ @item_provider = DSL::Items::ItemProvider.send(:new)
46
+ allow(DSL::Items::ItemProvider).to receive(:instance).and_return(@item_provider)
47
+ @thing_provider = DSL::Things::ThingProvider.send(:new)
48
+ allow(DSL::Things::ThingProvider).to receive(:instance).and_return(@thing_provider)
49
+ @item_channel_link_provider = DSL::Items::ItemChannelLinkProvider.send(:new)
50
+ allow(DSL::Items::ItemChannelLinkProvider).to receive(:instance).and_return(@item_channel_link_provider)
51
+ mr = Core::Items::Metadata::NamespaceHash.registry
52
+ @metadata_provider = Mocks::MetadataProvider.new(mr.managed_provider.get)
53
+ mr.add_provider(@metadata_provider)
54
+ mr.set_managed_provider(@metadata_provider)
55
+ tm = OSGi.service("org.openhab.core.thing.ThingManager")
56
+ tm.class.field_reader :storage
57
+ tm.storage.keys.each { |k| tm.storage.remove(k) } # rubocop:disable Style/HashEachMethods not a hash
58
+ @log_index = File.size(log_file)
59
+ profile_factory = Core::ProfileFactory.send(:new)
60
+ @profile_factory_registration = OSGi.register_service(profile_factory)
61
+ allow(Core::ProfileFactory).to receive(:instance).and_return(profile_factory)
62
+ stub_const("OpenHAB::Core::Timer", Mocks::Timer) if self.class.mock_timers?
63
+ log_line = "rspec #{example.location} # #{example.full_description}"
64
+ logger.info(log_line)
65
+ Logger.events.info(log_line)
66
+ end
67
+
68
+ config.after do
69
+ # remove rules created during the spec
70
+ (Core.rule_registry.all.map(&:uid) - @known_rules).each do |uid|
71
+ remove_rule(uid) if defined?(remove_rule)
72
+ end
73
+ $ir.remove_provider(@item_provider)
74
+ Core::Items::Proxy.reset_cache
75
+ $things.remove_provider(@thing_provider)
76
+ Core::Things::Proxy.reset_cache
77
+ registry = OSGi.service("org.openhab.core.thing.link.ItemChannelLinkRegistry")
78
+ registry.remove_provider(@item_channel_link_provider)
79
+ Core::Items::Metadata::NamespaceHash.registry.remove_provider(@metadata_provider)
80
+ @metadata_provider.restore_parent
81
+ @profile_factory_registration.unregister
82
+ timers.cancel_all
83
+ # timers and rules have already been canceled, so we can safely just
84
+ # wipe this
85
+ DSL::Items::TimedCommand.timed_commands.clear
86
+ Timecop.return
87
+ restore_autoupdate_items
88
+ Mocks::PersistenceService.instance.reset
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenHAB
4
+ module RSpec
5
+ # @!visibility private
6
+ module JRuby
7
+ # Basically org.jruby.embed.osgi.OSGiIsolatedScriptingContainer$BundleGetResources,
8
+ # but implemented in Ruby so that it doesn't have a hard dependency on
9
+ # org.osgi.bundle.Bundle -- which we may need to load!
10
+ class OSGiBundleClassLoader
11
+ include org.jruby.util.Loader
12
+
13
+ def initialize(bundle)
14
+ @bundle = bundle
15
+ end
16
+
17
+ def get_resource(path)
18
+ @bundle.get_resource(path)
19
+ end
20
+
21
+ def get_resources(path)
22
+ @bundle.get_resources(path)
23
+ end
24
+
25
+ def load_class(name)
26
+ @bundle.load_class(name)
27
+ end
28
+
29
+ def get_class_loader # rubocop:disable Naming/AccessorMethodName
30
+ @bundle&.adapt(org.osgi.framework.wiring.BundleWiring.java_class)&.class_loader
31
+ end
32
+ end
33
+
34
+ module InstanceConfig
35
+ def add_loader(loader)
36
+ # have to use Ruby-style class reference for the defined? check
37
+ if defined?(Java::OrgOsgiFramework::Bundle) && loader.is_a?(org.osgi.framework.Bundle)
38
+ loader = OSGiBundleClassLoader.new(loader)
39
+ end
40
+ super(loader)
41
+ end
42
+ end
43
+ org.jruby.RubyInstanceConfig.prepend(InstanceConfig)
44
+ end
45
+ end
46
+ end