fusuma 2.0.3 → 2.2.0

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.
@@ -24,9 +24,17 @@ module Fusuma
24
24
  updating_events = gesture_buffer.updating_events
25
25
  return if updating_events.empty?
26
26
 
27
- updating_time = 100 * (updating_events.last.time - updating_events.first.time)
28
- oneshot_move_x = gesture_buffer.sum_attrs(:move_x) / updating_time
29
- oneshot_move_y = gesture_buffer.sum_attrs(:move_y) / updating_time
27
+ oneshot_move_x, oneshot_move_y = if updating_events.size >= 10
28
+ updating_time = 100 * (updating_events[-1].time - updating_events[-10].time)
29
+ last_10 = gesture_buffer.class.new(updating_events[-10..-1])
30
+ [last_10.sum_attrs(:move_x) / updating_time,
31
+ last_10.sum_attrs(:move_y) / updating_time]
32
+ else
33
+ updating_time = 100 * (updating_events.last.time - updating_events.first.time)
34
+ [gesture_buffer.sum_attrs(:move_x) / updating_time,
35
+ gesture_buffer.sum_attrs(:move_y) / updating_time]
36
+ end
37
+ gesture_buffer.sum_attrs(:move_x) / updating_time
30
38
 
31
39
  finger = gesture_buffer.finger
32
40
  status = case gesture_buffer.events.last.record.status
@@ -14,19 +14,19 @@ module Fusuma
14
14
  end
15
15
 
16
16
  def execute(event)
17
- search_command(event).tap do |command|
18
- break unless command
17
+ command = search_command(event)
19
18
 
20
- MultiLogger.info(command: command, args: event.record.args)
19
+ MultiLogger.info(command: command, args: event.record.args)
21
20
 
22
- accel = args_accel(event)
23
- additional_env = event.record.args
24
- .deep_transform_keys(&:to_s)
25
- .deep_transform_values { |v| (v * accel).to_s }
21
+ accel = args_accel(event)
22
+ additional_env = event.record.args
23
+ .deep_transform_keys(&:to_s)
24
+ .deep_transform_values { |v| (v * accel).to_s }
26
25
 
27
- pid = Process.spawn(additional_env, command.to_s)
28
- Process.detach(pid)
29
- end
26
+ pid = Process.spawn(additional_env, command.to_s)
27
+ Process.detach(pid)
28
+ rescue SystemCallError => e
29
+ MultiLogger.error("#{event.record.index.keys}": e.message.to_s)
30
30
  end
31
31
 
32
32
  def executable?(event)
@@ -15,7 +15,7 @@ module Fusuma
15
15
  # @return [Array<Symbol>]
16
16
  def execute_keys
17
17
  # [name.split('Executors::').last.underscore.gsub('_executor', '').to_sym]
18
- raise NotImplementedError, "override #{name}##{__method__}"
18
+ raise NotImplementedError, "override #{self.class.name}##{__method__}"
19
19
  end
20
20
 
21
21
  # check executable
@@ -14,7 +14,7 @@ module Fusuma
14
14
  # @return [Records::GestureRecord, nil]
15
15
  def parse_record(record)
16
16
  case line = record.to_s
17
- when /GESTURE_SWIPE|GESTURE_PINCH/
17
+ when /GESTURE_SWIPE|GESTURE_PINCH|GESTURE_HOLD/
18
18
  gesture, status, finger, delta = parse_libinput(line)
19
19
  else
20
20
  return
@@ -32,13 +32,18 @@ module Fusuma
32
32
  _device, event_name, _time, other = line.strip.split(nil, 4)
33
33
  finger, other = other.split(nil, 2)
34
34
 
35
+ gesture, status = *detect_gesture(event_name)
36
+
37
+ status = 'cancelled' if gesture == 'hold' && status == 'end' && other == 'cancelled'
35
38
  delta = parse_delta(other)
36
- [*detect_gesture(event_name), finger, delta]
39
+ [gesture, status, finger, delta]
37
40
  end
38
41
 
39
42
  def detect_gesture(event_name)
40
- event_name =~ /GESTURE_(SWIPE|PINCH)_(BEGIN|UPDATE|END)/
41
- [Regexp.last_match(1).downcase, Regexp.last_match(2).downcase]
43
+ event_name =~ /GESTURE_(SWIPE|PINCH|HOLD)_(BEGIN|UPDATE|END)/
44
+ gesture = Regexp.last_match(1).downcase
45
+ status = Regexp.last_match(2).downcase
46
+ [gesture, status]
42
47
  end
43
48
 
44
49
  def parse_delta(line)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fusuma
4
- VERSION = '2.0.3'
4
+ VERSION = '2.2.0'
5
5
  end
data/lib/fusuma.rb CHANGED
@@ -29,6 +29,7 @@ module Fusuma
29
29
  end
30
30
 
31
31
  def read_options(option)
32
+ MultiLogger.filepath = option[:log_filepath]
32
33
  MultiLogger.instance.debug_mode = option[:verbose]
33
34
 
34
35
  load_custom_config(option[:config_path])
@@ -142,19 +143,26 @@ module Fusuma
142
143
  end
143
144
  main_events.sort_by! { |e| e.record.trigger_priority }
144
145
 
145
- condition = nil
146
+ matched_condition = nil
146
147
  matched_context = nil
147
148
  event = main_events.find do |main_event|
148
149
  matched_context = Config::Searcher.find_context(request_context) do
149
- condition, index_record = Config::Searcher.find_condition do
150
+ matched_condition, modified_record = Config::Searcher.find_condition do
150
151
  main_event.record.merge(records: modifiers.map(&:record))
151
152
  end
152
- main_event if index_record
153
+ if matched_condition && modified_record
154
+ main_event.record = modified_record
155
+ else
156
+ matched_condition, = Config::Searcher.find_condition do
157
+ Config.search(main_event.record.index) &&
158
+ Config.find_execute_key(main_event.record.index)
159
+ end
160
+ end
153
161
  end
154
162
  end
155
163
  return if event.nil?
156
164
 
157
- [condition, matched_context, event]
165
+ [matched_condition, matched_context, event]
158
166
  end
159
167
 
160
168
  # @param event [Plugin::Events::Event]
@@ -7,80 +7,162 @@ require './lib/fusuma/config/searcher'
7
7
  # spec for Config
8
8
  module Fusuma
9
9
  RSpec.describe Config::Searcher do
10
- let(:keymap) do
11
- {
12
- 'swipe' => {
13
- 3 => {
14
- 'left' => { 'command' => 'alt+Left' },
15
- 'right' => { 'command' => 'alt+Right' }
16
- },
17
- 4 => {
18
- 'left' => { 'command' => 'super+Left' },
19
- 'right' => { 'command' => 'super+Right' }
20
- }
21
- },
22
- 'pinch' => {
23
- 'in' => { 'command' => 'ctrl+plus' },
24
- 'out' => { 'command' => 'ctrl+minus' }
25
- }
26
- }
27
- end
10
+ around do |example|
11
+ ConfigHelper.load_config_yml = <<~CONFIG
12
+ swipe:
13
+ 3:
14
+ left:
15
+ command: 'alt+Left'
16
+ right:
17
+ command: 'alt+Right'
18
+ 4:
19
+ left:
20
+ command: 'super+Left'
21
+ right:
22
+ command: 'super+Right'
23
+ pinch:
24
+ in:
25
+ command: 'ctrl+plus'
26
+ out:
27
+ command: 'ctrl+minus'
28
+ CONFIG
28
29
 
29
- let(:keymap_without_finger) do
30
- {
31
- 'swipe' => {
32
- 'left' => { 'command' => 'alt+Left' }
33
- }
34
- }
35
- end
30
+ example.run
36
31
 
37
- describe '.custom_path=' do
38
- before { Singleton.__init__(Config) }
39
- it 'should reload keymap file' do
40
- keymap = Config.instance.keymap
41
- Config.custom_path = './spec/lib/dummy_config.yml'
42
- custom_keymap = Config.instance.keymap
43
- expect(keymap).not_to eq custom_keymap
44
- end
32
+ ConfigHelper.clear_config_yml
45
33
  end
46
34
 
47
35
  describe '.search' do
48
36
  let(:index) { nil }
49
- subject { Config::Searcher.new.search(index, location: keymap.deep_symbolize_keys) }
37
+ let(:location) { Config.instance.keymap[0] }
38
+ let(:search) { Config::Searcher.new.search(index, location: location) }
50
39
  context 'index correct order' do
51
40
  let(:index) { Config::Index.new %w[pinch in command] }
52
- it { is_expected.to eq 'ctrl+plus' }
41
+ it { expect(Config::Searcher.new.search(index, location: location)).to eq 'ctrl+plus' }
53
42
  end
54
43
 
55
- context 'index include skippable key' do
56
- let(:index) do
57
- Config::Index.new [
58
- Config::Index::Key.new('pinch'),
59
- Config::Index::Key.new(2, skippable: true),
60
- Config::Index::Key.new('out'),
61
- Config::Index::Key.new('command')
62
- ]
63
- end
64
- it { expect(Config::Searcher.skip { subject }).to eq 'ctrl+minus' }
44
+ context 'index incorrect order' do
45
+ let(:index) { Config::Index.new %w[in pinch 2 command] }
46
+ it { expect(Config::Searcher.new.search(index, location: location)).not_to eq 'ctrl+plus' }
65
47
  end
66
48
 
67
- context 'index include skippable key at first' do
68
- let(:index) do
69
- Config::Index.new [
70
- Config::Index::Key.new(:hoge, skippable: true),
71
- Config::Index::Key.new(:fuga, skippable: true),
72
- Config::Index::Key.new('pinch'),
73
- Config::Index::Key.new('in'),
74
- Config::Index::Key.new(:piyo, skippable: true),
75
- Config::Index::Key.new('command')
76
- ]
49
+ context 'with Skip condtions' do
50
+ context 'when index includes skippable key' do
51
+ let(:index) do
52
+ Config::Index.new [
53
+ Config::Index::Key.new('pinch'),
54
+ Config::Index::Key.new(2, skippable: true),
55
+ Config::Index::Key.new('out'),
56
+ Config::Index::Key.new('command')
57
+ ]
58
+ end
59
+ it 'detects ctrl+minus with skip' do
60
+ condition, value = Config::Searcher.find_condition do
61
+ Config::Searcher.new.search(index, location: location)
62
+ end
63
+ expect([condition, value]).to eq([:skip, 'ctrl+minus'])
64
+ end
77
65
  end
78
- it { expect(Config::Searcher.skip { subject }).to eq 'ctrl+plus' }
79
- end
80
66
 
81
- context 'index incorrect order' do
82
- let(:index) { Config::Index.new %w[in pinch 2 command] }
83
- it { is_expected.not_to eq 'ctrl+plus' }
67
+ context 'when index includes skippable key at first' do
68
+ let(:index) do
69
+ Config::Index.new [
70
+ Config::Index::Key.new(:hoge, skippable: true),
71
+ Config::Index::Key.new(:fuga, skippable: true),
72
+ Config::Index::Key.new('pinch'),
73
+ Config::Index::Key.new('in'),
74
+ Config::Index::Key.new(:piyo, skippable: true),
75
+ Config::Index::Key.new('command')
76
+ ]
77
+ end
78
+ it 'detects ctrl+plus with skip' do
79
+ condition, value = Config::Searcher.find_condition do
80
+ Config::Searcher.new.search(index, location: location)
81
+ end
82
+ expect([condition, value]).to eq([:skip, 'ctrl+plus'])
83
+ end
84
+ end
85
+
86
+ context 'with begin/update/end' do
87
+ around do |example|
88
+ ConfigHelper.load_config_yml = <<~CONFIG
89
+ swipe:
90
+ 3:
91
+ begin:
92
+ command: 'echo begin'
93
+ update:
94
+ command: 'echo update'
95
+ end:
96
+ command: 'echo end'
97
+ keypress:
98
+ LEFTCTRL:
99
+ command: 'echo end+ctrl'
100
+ CONFIG
101
+
102
+ example.run
103
+
104
+ ConfigHelper.clear_config_yml
105
+ end
106
+
107
+ context 'without keypress' do
108
+ let(:index) do
109
+ Config::Index.new [
110
+ Config::Index::Key.new(:swipe),
111
+ Config::Index::Key.new(3),
112
+ Config::Index::Key.new('left', skippable: true),
113
+ Config::Index::Key.new('end'),
114
+ Config::Index::Key.new('command')
115
+ ]
116
+ end
117
+
118
+ it 'detects with skip' do
119
+ condition, value = Config::Searcher.find_condition do
120
+ Config::Searcher.new.search(index, location: location)
121
+ end
122
+ expect([condition, value]).to eq([:skip, 'echo end'])
123
+ end
124
+ end
125
+ context 'with keypress' do
126
+ context 'with valid key existing in config.yml' do
127
+ let(:index) do
128
+ Config::Index.new [
129
+ Config::Index::Key.new(:swipe),
130
+ Config::Index::Key.new(3),
131
+ Config::Index::Key.new('left', skippable: true),
132
+ Config::Index::Key.new('end'),
133
+ Config::Index::Key.new('keypress', skippable: true),
134
+ Config::Index::Key.new('LEFTCTRL', skippable: true),
135
+ Config::Index::Key.new('command')
136
+ ]
137
+ end
138
+ it 'detects end+ctrl with skip' do
139
+ condition, value = Config::Searcher.find_condition do
140
+ Config::Searcher.new.search(index, location: location)
141
+ end
142
+ expect([condition, value]).to eq([:skip, 'echo end+ctrl'])
143
+ end
144
+ end
145
+ context 'with non-existing key not existing in config.yml' do
146
+ let(:index) do
147
+ Config::Index.new [
148
+ Config::Index::Key.new(:swipe),
149
+ Config::Index::Key.new(3),
150
+ Config::Index::Key.new('up', skippable: true),
151
+ Config::Index::Key.new('end'),
152
+ Config::Index::Key.new('keypress', skippable: true),
153
+ Config::Index::Key.new('LEFTSHIFT', skippable: true), # Invalid key
154
+ Config::Index::Key.new('command')
155
+ ]
156
+ end
157
+ it 'detects end with skip (fallback to no keypress)' do
158
+ condition, value = Config::Searcher.find_condition do
159
+ Config::Searcher.new.search(index, location: location)
160
+ end
161
+ expect([condition, value]).to eq([:skip, 'echo end'])
162
+ end
163
+ end
164
+ end
165
+ end
84
166
  end
85
167
  end
86
168
 
@@ -25,14 +25,6 @@ module Fusuma
25
25
  }
26
26
  end
27
27
 
28
- let(:keymap_without_finger) do
29
- {
30
- 'swipe' => {
31
- 'left' => { 'command' => 'alt+Left' }
32
- }
33
- }
34
- end
35
-
36
28
  describe '.custom_path=' do
37
29
  before { Singleton.__init__(Config) }
38
30
  it 'should reload keymap file' do
@@ -5,7 +5,9 @@ require './lib/fusuma/libinput_command'
5
5
 
6
6
  module Fusuma
7
7
  RSpec.describe LibinputCommand do
8
- let(:libinput_command) { described_class.new(libinput_options: libinput_options, commands: commands) }
8
+ let(:libinput_command) do
9
+ described_class.new(libinput_options: libinput_options, commands: commands)
10
+ end
9
11
  let(:libinput_options) { [] }
10
12
  let(:commands) { {} }
11
13
  describe '#version' do
@@ -66,7 +68,7 @@ module Fusuma
66
68
  context 'without NEW_CLI_OPTION_VERSION' do
67
69
  before do
68
70
  allow(libinput_command).to receive(:version)
69
- .and_return(LibinputCommand::NEW_CLI_OPTION_VERSION - 0.1)
71
+ .and_return('1.7')
70
72
  end
71
73
  it { is_expected.to eq false }
72
74
  end
@@ -79,7 +81,8 @@ module Fusuma
79
81
  before do
80
82
  dummy_io = StringIO.new('dummy')
81
83
  io = StringIO.new('dummy output')
82
- allow(Open3).to receive(:popen3).with(anything).and_return([dummy_io, io, dummy_io, dummy_io, nil])
84
+ allow(Open3).to receive(:popen3).with(anything).and_return([dummy_io, io, dummy_io,
85
+ dummy_io, nil])
83
86
  end
84
87
 
85
88
  context 'with the alternative command' do
@@ -119,7 +122,7 @@ module Fusuma
119
122
  describe 'debug_events' do
120
123
  before do
121
124
  @dummy_io = StringIO.new('dummy')
122
- allow(Process).to receive(:spawn).with(anything).and_return(0)
125
+ allow(Process).to receive(:detach).with(anything).and_return(nil)
123
126
  end
124
127
  subject { libinput_command.debug_events(@dummy_io) }
125
128
 
@@ -129,7 +132,8 @@ module Fusuma
129
132
  end
130
133
 
131
134
  it 'should call dummy events' do
132
- expect(Process).to receive(:spawn).with('dummy_debug_events', { out: @dummy_io, in: '/dev/null' }).once
135
+ expect(Process).to receive(:spawn).with('dummy_debug_events',
136
+ { out: @dummy_io, in: '/dev/null' }).once
133
137
  subject
134
138
  end
135
139
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
+ require 'rspec-parameterized'
4
5
 
5
6
  require './lib/fusuma/plugin/events/event'
6
7
  require './lib/fusuma/plugin/events/records/gesture_record'
@@ -13,7 +14,7 @@ module Fusuma
13
14
  before do
14
15
  @buffer = GestureBuffer.new
15
16
  delta = Events::Records::GestureRecord::Delta.new(-1, 0, 0, 0, 0, 0)
16
- @event_generator = lambda { |time = nil, status = 'updating'|
17
+ @event_generator = lambda { |time = nil, status = 'update'|
17
18
  Events::Event.new(time: time,
18
19
  tag: 'libinput_gesture_parser',
19
20
  record: Events::Records::GestureRecord.new(
@@ -53,20 +54,39 @@ module Fusuma
53
54
  end
54
55
 
55
56
  describe '#clear_expired' do
56
- it 'should keep only events generated within 30 seconds' do
57
- time = Time.now
58
- event1 = @event_generator.call(time)
59
- @buffer.buffer(event1)
60
- event2 = @event_generator.call(time + 100)
61
- event3 = @event_generator.call(time + 100)
62
- @buffer.buffer(event2)
63
- @buffer.buffer(event3)
64
-
65
- @buffer.clear_expired(current_time: time + 100.1)
66
-
67
- expect(@buffer.events).to eq [event2, event3]
57
+ context 'default' do
58
+ before do
59
+ @time = Time.now
60
+ event1 = @event_generator.call(@time)
61
+ @buffer.buffer(event1)
62
+ @event2 = @event_generator.call(@time + 100)
63
+ @event3 = @event_generator.call(@time + 100)
64
+ @buffer.buffer(@event2)
65
+ @buffer.buffer(@event3)
66
+ end
67
+ it 'should keep only events generated within 30 seconds' do
68
+ @buffer.clear_expired(current_time: @time + 100.1)
69
+ expect(@buffer.events).to eq [@event2, @event3]
70
+ end
71
+ context 'with cancelled or ended' do
72
+ where(:last_state, :want) do
73
+ [
74
+ ['end', []],
75
+ ['cancelled', []]
76
+ ]
77
+ end
78
+
79
+ with_them do
80
+ it 'should clear events' do
81
+ event4 = @event_generator.call(@time + 100, last_state)
82
+ @buffer.buffer(event4)
83
+
84
+ @buffer.clear_expired(current_time: @time + 100.1)
85
+ expect(@buffer.events).to eq want
86
+ end
87
+ end
88
+ end
68
89
  end
69
-
70
90
  context 'change seconds to keep' do
71
91
  around do |example|
72
92
  ConfigHelper.load_config_yml = <<~CONFIG
@@ -27,7 +27,7 @@ module Fusuma
27
27
 
28
28
  example.run
29
29
 
30
- Config.custom_path = nil
30
+ ConfigHelper.clear_config_yml
31
31
  end
32
32
 
33
33
  describe '#detect' do
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ require './lib/fusuma/plugin/detectors/hold_detector'
6
+ require './lib/fusuma/plugin/buffers/gesture_buffer'
7
+ require './lib/fusuma/plugin/events/records/gesture_record'
8
+ require './lib/fusuma/config'
9
+
10
+ module Fusuma
11
+ module Plugin
12
+ module Detectors
13
+ RSpec.describe HoldDetector do
14
+ before do
15
+ @detector = HoldDetector.new
16
+ @buffer = Buffers::GestureBuffer.new
17
+ @timer_buffer = Buffers::TimerBuffer.new
18
+ end
19
+
20
+ around do |example|
21
+ ConfigHelper.load_config_yml = <<~CONFIG
22
+ threshold:
23
+ hold: 1
24
+ CONFIG
25
+
26
+ example.run
27
+
28
+ Config.custom_path = nil
29
+ end
30
+
31
+ describe '#detect' do
32
+ context 'with no hold event in buffer' do
33
+ before do
34
+ @buffer.clear
35
+ end
36
+ it { expect(@detector.detect([@buffer, @timer_buffer])).to eq nil }
37
+ end
38
+
39
+ context 'with only hold begin event' do
40
+ before do
41
+ events = create_hold_events(statuses: ['begin'])
42
+ events.each { |event| @buffer.buffer(event) }
43
+ end
44
+ it { expect(@detector.detect([@buffer, @timer_buffer])).to be_a Events::Event }
45
+ it { expect(@detector.detect([@buffer, @timer_buffer]).record).to be_a Events::Records::IndexRecord }
46
+ it { expect(@detector.detect([@buffer, @timer_buffer]).record.index).to be_a Config::Index }
47
+ it 'should detect 3 fingers hold' do
48
+ event = @detector.detect([@buffer, @timer_buffer])
49
+ expect(event.record.index.keys.map(&:symbol)).to eq([:hold, 3, :begin])
50
+ end
51
+ end
52
+
53
+ context 'with hold events(begin,cancelled)' do
54
+ before do
55
+ events = create_hold_events(statuses: %w[begin cancelled])
56
+ events.each { |event| @buffer.buffer(event) }
57
+ end
58
+ it { expect(@detector.detect([@buffer, @timer_buffer])).to be_a Events::Event }
59
+ it { expect(@detector.detect([@buffer, @timer_buffer]).record).to be_a Events::Records::IndexRecord }
60
+ it { expect(@detector.detect([@buffer, @timer_buffer]).record.index).to be_a Config::Index }
61
+ it 'should detect 3 fingers hold canclled' do
62
+ event = @detector.detect([@buffer, @timer_buffer])
63
+ expect(event.record.index.keys.map(&:symbol)).to eq([:hold, 3, :cancelled])
64
+ end
65
+ end
66
+
67
+ context 'with hold events(begin,end)' do
68
+ before do
69
+ events = create_hold_events(statuses: %w[begin end])
70
+ events.each { |event| @buffer.buffer(event) }
71
+ end
72
+ it { expect(@detector.detect([@buffer, @timer_buffer])).to be_a Events::Event }
73
+ it { expect(@detector.detect([@buffer, @timer_buffer]).record).to be_a Events::Records::IndexRecord }
74
+ it { expect(@detector.detect([@buffer, @timer_buffer]).record.index).to be_a Config::Index }
75
+ it 'should detect 3 fingers hold' do
76
+ events = @detector.detect([@buffer, @timer_buffer])
77
+ expect(events.record.index.keys.map(&:symbol)).to eq([:hold, 3, :end])
78
+ end
79
+ end
80
+
81
+ context 'with hold events and timer events' do
82
+ context 'with begin event and timer events' do
83
+ before do
84
+ events = create_hold_events(statuses: %w[begin])
85
+ events.each { |event| @buffer.buffer(event) }
86
+ @time = events.last.time
87
+ @timer_buffer.buffer(create_timer_event(time: @time + HoldDetector::BASE_THERESHOLD))
88
+ end
89
+ it { expect(@detector.detect([@buffer, @timer_buffer])).to eq nil }
90
+
91
+ context 'with enough holding time' do
92
+ before do
93
+ @timer_buffer.clear
94
+ @timer_buffer.buffer(create_timer_event(time: @time + HoldDetector::BASE_THERESHOLD + 0.01))
95
+ end
96
+ it { expect(@detector.detect([@buffer, @timer_buffer])).to be_a Events::Event }
97
+ it { expect(@detector.detect([@buffer, @timer_buffer]).record).to be_a Events::Records::IndexRecord }
98
+ it { expect(@detector.detect([@buffer, @timer_buffer]).record.index).to be_a Config::Index }
99
+ it 'should detect 3 fingers hold' do
100
+ events = @detector.detect([@buffer, @timer_buffer])
101
+ expect(events.record.index.keys.map(&:symbol)).to eq([:hold, 3])
102
+ end
103
+ end
104
+ context 'with changing threshold' do
105
+ around do |example|
106
+ ConfigHelper.load_config_yml = <<~CONFIG
107
+ threshold:
108
+ hold: 0.9
109
+ CONFIG
110
+
111
+ example.run
112
+
113
+ Config.custom_path = nil
114
+ end
115
+
116
+ it { expect(@detector.detect([@buffer, @timer_buffer])).not_to eq nil }
117
+ it 'should detect 3 fingers hold' do
118
+ events = @detector.detect([@buffer, @timer_buffer])
119
+ expect(events.record.index.keys.map(&:symbol)).to eq([:hold, 3])
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def create_hold_events(statuses:)
129
+ record_type = HoldDetector::GESTURE_RECORD_TYPE
130
+ statuses.map do |status|
131
+ gesture_record = Events::Records::GestureRecord.new(status: status,
132
+ gesture: record_type,
133
+ finger: 3,
134
+ delta: nil)
135
+ Events::Event.new(tag: 'libinput_gesture_parser', record: gesture_record)
136
+ end
137
+ end
138
+
139
+ def create_timer_event(time: Time.now)
140
+ Events::Event.new(time: time, tag: 'timer_input', record: Events::Records::TextRecord.new('timer'))
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end