vigilem-evdev 0.1.3
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 +7 -0
- data/LICENSE.txt +22 -0
- data/ext/Rakefile +31 -0
- data/ext/rake_helper.rb +10 -0
- data/lib/vigilem/_evdev.rb +24 -0
- data/lib/vigilem/evdev.rb +14 -0
- data/lib/vigilem/evdev/at_exit.rb +8 -0
- data/lib/vigilem/evdev/context_filter.rb +92 -0
- data/lib/vigilem/evdev/demultiplexer.rb +26 -0
- data/lib/vigilem/evdev/device.rb +182 -0
- data/lib/vigilem/evdev/device_capabilities.rb +53 -0
- data/lib/vigilem/evdev/dom.rb +8 -0
- data/lib/vigilem/evdev/dom/adapter.rb +87 -0
- data/lib/vigilem/evdev/dom/code_values_tables.rb +172 -0
- data/lib/vigilem/evdev/dom/input_event_converter.rb +329 -0
- data/lib/vigilem/evdev/dom/input_event_utils.rb +48 -0
- data/lib/vigilem/evdev/dom/key_values_tables.rb +248 -0
- data/lib/vigilem/evdev/dom/kp_table.rb +52 -0
- data/lib/vigilem/evdev/focus_context_filter.rb +124 -0
- data/lib/vigilem/evdev/input_system_handler.rb +69 -0
- data/lib/vigilem/evdev/key_map_cache.rb +64 -0
- data/lib/vigilem/evdev/multiplexer.rb +73 -0
- data/lib/vigilem/evdev/system.rb +17 -0
- data/lib/vigilem/evdev/system/input.rb +1053 -0
- data/lib/vigilem/evdev/system/input/event.rb +4 -0
- data/lib/vigilem/evdev/system/input/input_event.rb +33 -0
- data/lib/vigilem/evdev/system/int.rb +9 -0
- data/lib/vigilem/evdev/system/ioctl.rb +143 -0
- data/lib/vigilem/evdev/system/keymap_loaders.rb +89 -0
- data/lib/vigilem/evdev/system/keymap_loaders/dumpkeys_loader.rb +98 -0
- data/lib/vigilem/evdev/system/keymap_loaders/kmap_loader.rb +74 -0
- data/lib/vigilem/evdev/system/posix_types.rb +5 -0
- data/lib/vigilem/evdev/system/time.rb +21 -0
- data/lib/vigilem/evdev/transfer_agent.rb +40 -0
- data/lib/vigilem/evdev/version.rb +5 -0
- data/lib/vigilem/evdev/vty_context_filter.rb +216 -0
- data/spec/after_each_example_group.rb +29 -0
- data/spec/delete_test_cache_after_group.rb +26 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/vigilem/_evdev_spec.rb +11 -0
- data/spec/vigilem/evdev/context_filter_spec.rb +114 -0
- data/spec/vigilem/evdev/demultiplexer_spec.rb +44 -0
- data/spec/vigilem/evdev/device_capabilities_spec.rb +0 -0
- data/spec/vigilem/evdev/device_spec.rb +97 -0
- data/spec/vigilem/evdev/dom/adapter_spec.rb +5 -0
- data/spec/vigilem/evdev/dom/input_event_converter_spec.rb +253 -0
- data/spec/vigilem/evdev/dom/input_event_utils_spec.rb +23 -0
- data/spec/vigilem/evdev/focus_context_filter_spec.rb +112 -0
- data/spec/vigilem/evdev/input_system_handler_spec.rb +146 -0
- data/spec/vigilem/evdev/key_map_cache_spec.rb +65 -0
- data/spec/vigilem/evdev/multiplexer_spec.rb +55 -0
- data/spec/vigilem/evdev/system/input/input_event_spec.rb +33 -0
- data/spec/vigilem/evdev/system/input_spec.rb +274 -0
- data/spec/vigilem/evdev/system/int_spec.rb +30 -0
- data/spec/vigilem/evdev/system/ioctl_spec.rb +206 -0
- data/spec/vigilem/evdev/system/keymap_loaders/dumpkeys_loader_spec.rb +5 -0
- data/spec/vigilem/evdev/system/keymap_loaders/kmap_loader_spec.rb +24 -0
- data/spec/vigilem/evdev/system/keymap_loaders_spec.rb +22 -0
- data/spec/vigilem/evdev/system/posix_types_spec.rb +15 -0
- data/spec/vigilem/evdev/system/time_spec.rb +18 -0
- data/spec/vigilem/evdev/transfer_agent_spec.rb +57 -0
- data/spec/vigilem/evdev/vty_context_filter_spec.rb +282 -0
- metadata +286 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'vigilem/support'
|
2
|
+
|
3
|
+
require 'vigilem/evdev/system/posix_types'
|
4
|
+
|
5
|
+
module Vigilem
|
6
|
+
module Evdev
|
7
|
+
module Time
|
8
|
+
# represents timeval from time.h
|
9
|
+
class Timeval < ::VFFIStruct
|
10
|
+
layout_with_methods :tv_sec, :__kernel_time_t,
|
11
|
+
:tv_usec, :__kernel_suseconds_t
|
12
|
+
|
13
|
+
#
|
14
|
+
# @return [Numeric]
|
15
|
+
def to_f
|
16
|
+
tv_sec.to_f + (tv_usec.to_f/1000000.0)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'vigilem/core/transfer_agent'
|
2
|
+
|
3
|
+
require 'vigilem/evdev/demultiplexer'
|
4
|
+
|
5
|
+
require 'vigilem/evdev/multiplexer'
|
6
|
+
|
7
|
+
module Vigilem
|
8
|
+
module Evdev
|
9
|
+
#
|
10
|
+
#
|
11
|
+
class TransferAgent < Core::TransferAgent
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# @todo move away from singleton and look at the one used by x11
|
16
|
+
# @param args [Hash]
|
17
|
+
# @param :inputs [Array]
|
18
|
+
# @param :outputs [Hash{observer_object => {event_opts}]
|
19
|
+
# @see Demultiplexer#add_oberver
|
20
|
+
# @return [TransferAgent]
|
21
|
+
def acquire(args={})
|
22
|
+
if @transfer_agent
|
23
|
+
@transfer_agent.multiplexer.add_inputs(*args[:inputs]) if args[:inputs]
|
24
|
+
@transfer_agent.add_observers(args[:outputs]) if args[:outputs]
|
25
|
+
if ((din = @transfer_agent.demultiplexer.input) || (mout = @transfer_agent.multiplexer.out)).nil? or not mout.equal?(din)
|
26
|
+
@transfer_agent.demultiplexer.input = @transfer_agent.multiplexer.out = (mout || din || [])
|
27
|
+
end
|
28
|
+
@transfer_agent
|
29
|
+
else
|
30
|
+
m = Multiplexer.acquire(args[:inputs])
|
31
|
+
d = Demultiplexer.acquire(args[:outputs])
|
32
|
+
@transfer_agent = new(d, m)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'vigilem/support/core_ext'
|
2
|
+
|
3
|
+
require 'vigilem/support/key_map'
|
4
|
+
|
5
|
+
require 'vigilem/_evdev'
|
6
|
+
|
7
|
+
require 'vigilem/evdev/context_filter'
|
8
|
+
|
9
|
+
module Vigilem
|
10
|
+
module Evdev
|
11
|
+
#
|
12
|
+
# filters events if the VTY differs from the one of this process
|
13
|
+
class VTYContextFilter
|
14
|
+
|
15
|
+
KeyMap = Support::KeyMap
|
16
|
+
|
17
|
+
include ContextFilter
|
18
|
+
# @todo make these class_methods
|
19
|
+
attr_reader :current_console_num, :console_keys,
|
20
|
+
:current_console_keys, :other_console_keys
|
21
|
+
|
22
|
+
#
|
23
|
+
# @param [KeyMap] keymap
|
24
|
+
# @return
|
25
|
+
def initialize(keymap=Evdev.key_map)
|
26
|
+
@key_map = keymap unless keymap.eql? Evdev.key_map
|
27
|
+
|
28
|
+
@current_console_num = `fgconsole`.rstrip.to_i
|
29
|
+
curr_con_keys, oth_con_keys =
|
30
|
+
self.class.console_keys(keymap).partition {|syms,v| v == "Console_#{@current_console_num}" }
|
31
|
+
@current_console_keys, @other_console_keys = KeyMap[curr_con_keys], KeyMap[oth_con_keys]
|
32
|
+
|
33
|
+
@current_console_keys.left_side_aliases(:keycode, :keycodes)
|
34
|
+
@current_console_keys.right_side_aliases(:keysym, :keysyms)
|
35
|
+
|
36
|
+
@other_console_keys.left_side_aliases(:keycode, :keycodes)
|
37
|
+
@other_console_keys.right_side_aliases(:keysym, :keysyms)
|
38
|
+
|
39
|
+
@current_keys = []
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# @param [KeyMap] keymap
|
44
|
+
# @return [KeyMap]
|
45
|
+
def self.console_keys(keymap)
|
46
|
+
@console_keys = if keymap.metadata[:loader].class.name =~ /KmapLoader/
|
47
|
+
keymap.select {|k,v| v =~ /console_\d+/i }
|
48
|
+
else
|
49
|
+
console_syms = keymap.info.keysyms.select {|k,v| v =~ /console_\d+/i }
|
50
|
+
tmp = keymap.select {|k,v| v =~ /^#{console_syms.keys.join('|')}$/ }
|
51
|
+
tmp.each {|k,v| tmp[k] = console_syms[v] }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# @return [KeyMap]
|
57
|
+
def key_map
|
58
|
+
@key_map || Evdev.key_map
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# @return [Array]
|
63
|
+
def current_keys
|
64
|
+
if @current_keys and current_keys_changed?
|
65
|
+
max_and_one = KeyMap.mod_weights.values.max + 1
|
66
|
+
@current_keys.sort_by! {|ksym| KeyMap.mod_weights[ksym.downcase] || max_and_one }
|
67
|
+
@current_keys_hash_cache = @current_keys.hash
|
68
|
+
@current_keys
|
69
|
+
else
|
70
|
+
if @current_keys.nil?
|
71
|
+
@current_keys = []
|
72
|
+
@current_keys_hash_cache = @current_keys.hash
|
73
|
+
end
|
74
|
+
@current_keys
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# @param [Array<System::InputEvent>] events
|
80
|
+
# @return [Array]
|
81
|
+
def process(*events)
|
82
|
+
events.select do |event|
|
83
|
+
off_status = off?
|
84
|
+
key_check = if event.type == System::Input::EV_KEY
|
85
|
+
if event.value == 1
|
86
|
+
add_key(keysym_or_keycode(event.code))
|
87
|
+
off_status
|
88
|
+
elsif event.value == 0
|
89
|
+
kork = keysym_or_keycode(event.code)
|
90
|
+
release_status = allow_release?(kork)
|
91
|
+
remove_key(kork)
|
92
|
+
release_status
|
93
|
+
end
|
94
|
+
end
|
95
|
+
if key_check.nil? then off_status else key_check end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# @param key
|
101
|
+
# @return [Array]
|
102
|
+
def add_key(keycode_or_keysym)
|
103
|
+
current_keys << keycode_or_keysym
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# @param [Fixnum] key_num
|
108
|
+
# @return
|
109
|
+
def remove_key(keycode_or_keysym)
|
110
|
+
current_keys.delete(keycode_or_keysym)
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# @todo name
|
115
|
+
# @return [String]
|
116
|
+
def keysym_or_keycode(key_num)
|
117
|
+
sym = keysym(keycode = "keycode#{key_num}")
|
118
|
+
if sym =~ /shift|alt|c(on)?tro?l|capsshift/i
|
119
|
+
sym
|
120
|
+
else
|
121
|
+
keycode
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# @todo move to KeyMapUtils, or something like that
|
126
|
+
# @param [Integer] key_num
|
127
|
+
# @return [String]
|
128
|
+
def keysym(keycode)
|
129
|
+
if key_map.metadata[:loader].class.name =~ /KmapLoader/
|
130
|
+
key_map.keysym(keycode)
|
131
|
+
else
|
132
|
+
key_map.info.keysyms[key_map.char_ref(keycode)]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# @return [TrueClass || FalseClass || NilClass]
|
138
|
+
def on?
|
139
|
+
if changed_vty?
|
140
|
+
self.filter_ommisions = current_keys_downcase
|
141
|
+
on_change(ON)
|
142
|
+
elsif reverted_vty?
|
143
|
+
self.filter_ommisions = current_keys_downcase
|
144
|
+
on_change(OFF)
|
145
|
+
end
|
146
|
+
was_on?
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# @return [TrueClass || FalseClass]
|
151
|
+
def reverted_vty?
|
152
|
+
was_on? and current_console_keys.keysym(current_keys_downcase)
|
153
|
+
end
|
154
|
+
|
155
|
+
#
|
156
|
+
# @return [TrueClass || FalseClass]
|
157
|
+
def changed_vty?
|
158
|
+
was_off? and other_console_keys.keysym(current_keys_downcase)
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# @return [TrueClass || FalseClass]
|
163
|
+
def off?
|
164
|
+
not on?
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
#
|
169
|
+
# @return [Integer] the hash of current_keys last time it was set
|
170
|
+
def current_keys_hash_cache
|
171
|
+
@current_keys_hash_cache ||= @current_keys.hash
|
172
|
+
end
|
173
|
+
|
174
|
+
#
|
175
|
+
# @return [TrueClass || FalseClass] if the cached value of
|
176
|
+
# #current_keys.hash changed
|
177
|
+
def current_keys_changed?
|
178
|
+
current_keys_hash_cache != @current_keys.hash
|
179
|
+
end
|
180
|
+
|
181
|
+
# @todo name!
|
182
|
+
# @return [Array]
|
183
|
+
def current_keys_downcase
|
184
|
+
downcase_hash ||= 0
|
185
|
+
if @current_keys_downcase.nil? or current_keys_hash_cache != downcase_hash
|
186
|
+
downcase_hash = current_keys_hash_cache
|
187
|
+
@current_keys_downcase = current_keys.map(&:downcase)
|
188
|
+
else
|
189
|
+
@current_keys_downcase
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
attr_writer :filter_ommisions
|
195
|
+
|
196
|
+
#
|
197
|
+
# @param other
|
198
|
+
# @return [TrueClass || FalseClass]
|
199
|
+
def allow_release?(keycode_or_keysym)
|
200
|
+
lower_kork = keycode_or_keysym.downcase
|
201
|
+
if filter_ommisions.include? lower_kork
|
202
|
+
filter_ommisions.delete(lower_kork)
|
203
|
+
on?
|
204
|
+
else
|
205
|
+
off?
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
# @return [Array]
|
211
|
+
def filter_ommisions
|
212
|
+
@filter_ommisions ||= []
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# fires after every example_group
|
3
|
+
module AfterEachExampleGroup
|
4
|
+
def after_each_example_group?
|
5
|
+
@after_each_example_group_flag ||= false
|
6
|
+
end
|
7
|
+
|
8
|
+
def after_each_example_group(&block)
|
9
|
+
if block
|
10
|
+
@after_each_example_group_flag = true
|
11
|
+
@after_each_example_group = block
|
12
|
+
else
|
13
|
+
@after_each_example_group
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
|
20
|
+
config.before :all do |example_group|
|
21
|
+
if ((egc = example_group.class).respond_to? :after_each_example_group? and egc.after_each_example_group?)
|
22
|
+
(egc.descendants - [egc]).map do |eg|
|
23
|
+
eg.append_after(:context, &egc.after_each_example_group)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
config.extend AfterEachExampleGroup
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
|
3
|
+
config.before :all do |example_group|
|
4
|
+
klass = example_group.class
|
5
|
+
|
6
|
+
(klass.descendants - [klass]).each do |egc|
|
7
|
+
if egc.metadata.key?(:clean_up_test_cache)
|
8
|
+
require 'fileutils'
|
9
|
+
require 'vigilem/evdev/key_map_cache'
|
10
|
+
test_name = 'key_map_cache_test'
|
11
|
+
egc.before(:each) do
|
12
|
+
Vigilem::Evdev::KeyMapCache.default_path = nil
|
13
|
+
Vigilem::Evdev::KeyMapCache.default_filename = test_name
|
14
|
+
end
|
15
|
+
egc.after(:each) do
|
16
|
+
Vigilem::Evdev::KeyMapCache.default_path = nil
|
17
|
+
Vigilem::Evdev::KeyMapCache.default_filename = nil
|
18
|
+
pth = File.join(Bundler.root, 'data', test_name)
|
19
|
+
FileUtils.remove(pth) if File.exists?(pth)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
event_files = Dir["/dev/input/event*"]
|
2
|
+
|
3
|
+
if event_files.empty?
|
4
|
+
puts %q<can't find character devices in /dev/input/event*>
|
5
|
+
exit
|
6
|
+
elsif not event_files.any? {|f| File.readable?(f) }
|
7
|
+
puts "Your user account isn't allowed to read from /dev/input/event*. please rerun as sudo"
|
8
|
+
exit
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'bundler'
|
12
|
+
Bundler.setup
|
13
|
+
|
14
|
+
require 'timeout'
|
15
|
+
|
16
|
+
require 'inline'
|
17
|
+
|
18
|
+
require 'vigilem/support/core_ext/debug_puts'
|
19
|
+
|
20
|
+
require 'vigilem/support/patch/ffi/pointer'
|
21
|
+
|
22
|
+
require 'after_each_example_group'
|
23
|
+
|
24
|
+
require 'delete_test_cache_after_group'
|
25
|
+
|
26
|
+
$ver = `uname -r`.split('-').first
|
27
|
+
|
28
|
+
$major = (ary = $ver.split('.')).first.to_i
|
29
|
+
|
30
|
+
$minor = ary[1].to_i
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'vigilem/evdev/context_filter'
|
2
|
+
|
3
|
+
describe Vigilem::Evdev::ContextFilter do
|
4
|
+
|
5
|
+
class ContextFilterHost
|
6
|
+
include Vigilem::Evdev::ContextFilter
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { ContextFilterHost.new }
|
10
|
+
|
11
|
+
describe '::included' do
|
12
|
+
it 'extends Forwardable' do
|
13
|
+
expect(subject.class.singleton_class.included_modules).to include(Forwardable)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#last_known_state' do
|
18
|
+
it 'defaults to nil/UKNOWN' do
|
19
|
+
expect(subject.last_known_state).to be_nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#was_on?' do
|
24
|
+
it 'compares ON to #last_known_state defaulting to false' do
|
25
|
+
expect(subject.was_on?).to be_falsey
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns true if #last_known_state == ON' do
|
29
|
+
subject.send(:last_known_state=, described_class::ON)
|
30
|
+
expect(subject.was_on?).to be_truthy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#was_off?' do
|
35
|
+
it 'compares OFF to #last_known_state defaulting to true' do
|
36
|
+
expect(subject.was_off?).to be_truthy
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns true if #last_known_state == OFF' do
|
40
|
+
subject.send(:last_known_state=, described_class::ON)
|
41
|
+
expect(subject.was_off?).to be_falsey
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#on?' do
|
46
|
+
it 'raises error because it needs to be overridden' do
|
47
|
+
expect { subject.on? }.to raise_error(NotImplementedError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#alias_method :filtered?, :on?
|
52
|
+
|
53
|
+
|
54
|
+
describe '#off?' do
|
55
|
+
it 'raises error because it needs to be overridden' do
|
56
|
+
expect { subject.off? }.to raise_error(NotImplementedError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#on_change' do
|
61
|
+
it 'updates #last_known_state' do
|
62
|
+
subject.on_change(described_class::ON)
|
63
|
+
expect(subject.last_known_state).to eql(described_class::ON)
|
64
|
+
end
|
65
|
+
|
66
|
+
class FakeObserver
|
67
|
+
def update(type, value)
|
68
|
+
(@updates ||= []).concat([type, value])
|
69
|
+
end
|
70
|
+
attr_reader :updates
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'notifies observers' do
|
74
|
+
fo = FakeObserver.new
|
75
|
+
subject.add_observer(fo)
|
76
|
+
subject.on_change(described_class::ON)
|
77
|
+
expect(fo.updates).to eql([subject, described_class::ON])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'private' do
|
82
|
+
describe '#last_known_state=' do
|
83
|
+
it 'sets #last_known_state' do
|
84
|
+
subject.send(:last_known_state=, described_class::ON)
|
85
|
+
expect(subject.last_known_state).to eql(described_class::ON)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#was_on=' do
|
90
|
+
it 'sets #was_on?' do
|
91
|
+
subject.send(:was_on=, false)
|
92
|
+
expect(subject.was_on?).to be_falsey
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'updates #last_known_state' do
|
96
|
+
subject.send(:was_on=, true)
|
97
|
+
expect(subject.last_known_state).to eql(described_class::ON)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#was_off=' do
|
102
|
+
it 'sets #was_off?' do
|
103
|
+
subject.send(:was_off=, true)
|
104
|
+
expect(subject.was_off?).to be_truthy
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'updates #last_known_state' do
|
108
|
+
subject.send(:was_off=, true)
|
109
|
+
expect(subject.last_known_state).to eql(described_class::OFF)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|