Module: OpenHAB::RSpec::Helpers

Defined in:
lib/openhab/rspec/helpers.rb

Overview

Provides helper methods for use in specs, to easily work with and adjust the openHAB environment.

These methods are automatically available in RSpec spec blocks, as well as other per-spec hooks like ‘before` and `after`. You can also call them explicitly.

Class Method Summary collapse

Class Method Details

.autorequiresvoid

This method returns an undefined value.

Require all files configured to be autorequired with the jrubyscripting addon in openHAB.

This method is normally called by RSpec hooks.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/openhab/rspec/helpers.rb', line 195

def autorequires
  ENV["RUBYLIB"] ||= ""
  ENV["RUBYLIB"] += ":" unless ENV["RUBYLIB"].empty?
  ENV["RUBYLIB"] += rubylib_dirs.join(":")

  $LOAD_PATH.unshift(*ENV["RUBYLIB"]
    .split(File::PATH_SEPARATOR)
      .reject(&:empty?)
      .reject do |path|
                       $LOAD_PATH.include?(path)
                     end)

  requires = jrubyscripting_config&.get("require") || ""
  requires.split(",").each do |f|
    require f.strip
  end
end

.autoupdate_all_itemsvoid

Deprecated.

This method returns an undefined value.

Reconfigure all items to autoupdate

To bypass any items configured to not autoupdate, waiting for the binding to update them.



81
82
83
# File 'lib/openhab/rspec/helpers.rb', line 81

def autoupdate_all_items
  # no-op
end

.execute_timersvoid

This method returns an undefined value.

Execute all pending timers



119
120
121
122
123
124
125
126
# File 'lib/openhab/rspec/helpers.rb', line 119

def execute_timers
  raise "Cannot execute timers when timers aren't mocked" unless self.class.mock_timers?

  now = ZonedDateTime.now
  DSL::TimerManager.instance.instance_variable_get(:@timers).each_key do |t|
    t.execute if t.active? && t.execution_time <= now
  end
end

.initialize_missing_thing_typesvoid

This method returns an undefined value.

Force things to come online that are missing their thing type

As of openHAB 4.0, things that are missing their thing type will not come online immediately. This especially impacts bindings that dynamically generate their thing types, but don’t persist those thing types. You can use this method to force them to come online immediately.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/openhab/rspec/helpers.rb', line 96

def initialize_missing_thing_types
  thing_manager = OpenHAB::OSGi.service("org.openhab.core.thing.ThingManager")
  thing_manager.class.field_reader :missingPrerequisites
  first = true
  thing_manager.missingPrerequisites.each_value do |prereq|
    if first
      prereq.class.field_accessor :timesChecked
      first = false
    end
    prereq.timesChecked = 60
  end
  m = thing_manager.class.java_class.get_declared_method(:checkMissingPrerequisites)
  m.accessible = true
  suspend_rules do
    m.invoke(thing_manager)
  end
end

.install_addon(addon_id, wait: true, ready_markers: nil) ⇒ void

This method returns an undefined value.

Install an openHAB addon

Parameters:

  • addon_id (String)

    The addon id, such as “binding-mqtt”

  • wait (true, false) (defaults to: true)

    Wait until OSGi has confirmed the bundle is installed and running before returning.

  • ready_markers (String, Array<String>) (defaults to: nil)

    Array of ready marker types to wait for. The addon’s bundle id is used as the identifier.



365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/openhab/rspec/helpers.rb', line 365

def install_addon(addon_id, wait: true, ready_markers: nil)
  service_filter = "(component.name=org.openhab.core.karafaddons)"
  addon_service = OSGi.service("org.openhab.core.addon.AddonService", filter: service_filter)
  addon_service.install(addon_id)
  return unless wait

  addon = nil
  loop do
    addon = addon_service.get_addon(addon_id, nil)
    break if addon.installed?

    sleep 0.25
  end

  return unless ready_markers

  package_id = addon.logger_packages.first

  ready_markers = Array(ready_markers).map do |marker|
    case marker
    when String
      org.openhab.core.service.ReadyMarker.new(marker, package_id)
    else
      marker
    end
  end

  rs = OSGi.service("org.openhab.core.service.ReadyService")
  loop do
    break if ready_markers.all? { |rm| rs.ready?(rm) }

    sleep 0.25
  end
end

.launch_karaf(include_bindings: true, include_jsondb: true, private_confdir: false, use_root_instance: false) ⇒ void

This method returns an undefined value.

Launch the karaf instance

This method is normally called by RSpec hooks.

See Also:



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/openhab/rspec/helpers.rb', line 221

def launch_karaf(include_bindings: true,
                 include_jsondb: true,
                 private_confdir: false,
                 use_root_instance: false)
  karaf = Karaf.new("#{Dir.pwd}/.karaf")
  karaf.include_bindings = include_bindings
  karaf.include_jsondb = include_jsondb
  karaf.private_confdir = private_confdir
  karaf.use_root_instance = use_root_instance
  main = karaf.launch

  require "openhab/dsl"

  require_relative "mocks/persistence_service"
  require_relative "mocks/timer"

  # override several DSL methods
  require_relative "openhab/core/items/proxy"
  require_relative "openhab/core/things/proxy"
  require_relative "openhab/core/actions"

  ps = Mocks::PersistenceService.instance
  persistence_bundle = org.osgi.framework.FrameworkUtil
                          .get_bundle(org.openhab.core.persistence.PersistenceService.java_class)
  persistence_bundle.bundle_context.register_service(org.openhab.core.persistence.PersistenceService.java_class,
                                                     ps,
                                                     nil)

  rs = OSGi.service("org.openhab.core.service.ReadyService")

  # wait for the rule engine
  filter = org.openhab.core.service.ReadyMarkerFilter.new
              .with_type(org.openhab.core.service.StartLevelService::STARTLEVEL_MARKER_TYPE)
              .with_identifier(org.openhab.core.service.StartLevelService::STARTLEVEL_RULEENGINE.to_s)

  karaf.send(:wait) do |continue|
    rs.register_tracker(org.openhab.core.service.ReadyService::ReadyTracker.impl { continue.call }, filter)
  end

  begin
    # load storage based type providers
    ast = org.openhab.core.thing.binding.AbstractStorageBasedTypeProvider
    ast_bundle = org.osgi.framework.FrameworkUtil.get_bundle(ast.java_class)
    storage_service = OSGi.service("org.openhab.core.storage.StorageService")
    require_relative "mocks/abstract_storage_based_type_provider_wrapped_storage_service"

    OSGi.bundle_context.bundles.each do |bundle|
      OSGi.service_component_classes(bundle)
          .select { |klass, _services| klass.ancestors.include?(ast.java_class) }
          .each do |klass, services|
        new_ast_klass = Class.new(ast)
        new_ast_klass.become_java!
        wrapped_storage_service = Mocks::AbstractStorageBasedTypeProviderWrappedStorageService
                                  .new(storage_service,
                                       new_ast_klass.java_class,
                                       klass)
        new_ast = new_ast_klass.new(wrapped_storage_service)

        services -= [klass.name]
        OSGi.register_service(new_ast, *services, bundle: ast_bundle)
      end
    end
  rescue NameError
    # @deprecated OH 4.0
  end

  # RSpec additions
  require_relative "suspend_rules"

  if defined?(::RSpec)
    ::RSpec.configure do |config|
      config.include OpenHAB::DSL
    end
  end
  main
rescue Exception => e
  puts e.inspect
  puts e.backtrace
  raise
end

.load_rulesvoid

This method returns an undefined value.

Load all Ruby rules in the config/automation directory

This method is normally called by RSpec hooks.



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/openhab/rspec/helpers.rb', line 309

def load_rules
  automation_paths = Array(::RSpec.configuration.openhab_automation_search_paths)

  lib_dirs = rubylib_dirs.map { |d| File.join(d, "") }
  lib_dirs << File.join(gem_home, "")

  SuspendRules.suspend_rules do
    files = automation_paths.map { |p| Dir["#{p}/**/*.rb"] }.flatten
    files.reject! do |f|
      lib_dirs.any? { |l| f.start_with?(l) }
    end
    files.sort_by { |f| [get_start_level(f), f] }.each do |f|
      load f
    rescue Exception => e
      warn "Failed loading #{f}: #{e.inspect}"
      warn e.backtrace
    end
  end
end

.load_transformsvoid

This method returns an undefined value.

Load all Ruby transformations in the config/transform directory

Since Ruby transformations must end with the .script extension, you must include an Emacs modeline comment (‘# -*- mode: ruby -*-`) in your script for it to be recognized.

This method is normally called by RSpec hooks.



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/openhab/rspec/helpers.rb', line 340

def load_transforms
  transform_path = "#{org.openhab.core.OpenHAB.config_folder}/transform"
  Dir["#{transform_path}/**/*.script"].each do |filename|
    script = File.read(filename)
    next unless ruby_file?(script)

    filename.slice!(0..transform_path.length)
    dir = File.dirname(filename)
    modules = (dir == ".") ? [] : moduleize(dir)
    basename = File.basename(filename)
    method = basename[0...-7]
    modules << method
    Transform.add_script(modules, script)
  end
end

.log_fileString

Returns The filename of the openHAB log.

Returns:

  • (String)

    The filename of the openHAB log.



401
402
403
# File 'lib/openhab/rspec/helpers.rb', line 401

def log_file
  "#{java.lang.System.get_property("openhab.logdir", nil)}/openhab.log"
end

.spec_log_linesArray<String>

Returns The log lines since this spec started.

Examples:

it "logs" do
  logger.trace("log line")
  expect(spec_log_lines).to include(match(/TRACE.*log line/))
end

Returns:

  • (Array<String>)

    The log lines since this spec started.



414
415
416
417
418
419
# File 'lib/openhab/rspec/helpers.rb', line 414

def spec_log_lines
  File.open(log_file, "rb") do |f|
    f.seek(@log_index) if @log_index
    f.read.split("\n")
  end
end

.suspend_rules(&block) ⇒ Object

Suspend rules for the duration of the block

Returns:

  • (Object)

    The return value from the block.



150
151
152
# File 'lib/openhab/rspec/helpers.rb', line 150

def suspend_rules(&block)
  SuspendRules.suspend_rules(&block)
end

.time_travel_and_execute_timers(duration) ⇒ void

This method returns an undefined value.

Wait ‘duration` seconds, then execute any pending timers

If timers are mocked, it will use Timecop. If they’re not mocked, it will just sleep for ‘duration`



136
137
138
139
140
141
142
143
# File 'lib/openhab/rspec/helpers.rb', line 136

def time_travel_and_execute_timers(duration)
  if self.class.mock_timers?
    Timecop.frozen? ? Timecop.freeze(duration) : Timecop.travel(duration)
    execute_timers
  else
    sleep duration
  end
end

.trigger_channel(channel, event = "") ⇒ void

This method returns an undefined value.

Manually send an event to a trigger channel

Parameters:



181
182
183
184
185
186
# File 'lib/openhab/rspec/helpers.rb', line 181

def trigger_channel(channel, event = "")
  channel = org.openhab.core.thing.ChannelUID.new(channel) if channel.is_a?(String)
  channel = channel.uid if channel.is_a?(org.openhab.core.thing.Channel)
  thing = channel.thing
  thing.handler.callback.channel_triggered(nil, channel, event)
end

.wait(how_long = 2.seconds) { ... } ⇒ void

This method returns an undefined value.

Calls the block repeatedly until the expectations inside pass.

Parameters:

  • how_long (Duration) (defaults to: 2.seconds)

    how long to keep trying before giving up

Yields:



160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/openhab/rspec/helpers.rb', line 160

def wait(how_long = 2.seconds)
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)

  begin
    yield
  rescue ::RSpec::Expectations::ExpectationNotMetError,
         ::RSpec::Mocks::MockExpectationError
    raise if Process.clock_gettime(Process::CLOCK_MONOTONIC) > start + how_long.to_f

    sleep 0.1
    retry
  end
end