fusuma 2.0.0.pre2 → 2.0.4

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -6
  3. data/fusuma.gemspec +3 -5
  4. data/lib/fusuma/config/searcher.rb +2 -0
  5. data/lib/fusuma/device.rb +3 -3
  6. data/lib/fusuma/environment.rb +1 -0
  7. data/lib/fusuma/libinput_command.rb +12 -12
  8. data/lib/fusuma/plugin/executors/command_executor.rb +10 -10
  9. data/lib/fusuma/plugin/inputs/input.rb +1 -0
  10. data/lib/fusuma/plugin/inputs/libinput_command_input.rb +9 -2
  11. data/lib/fusuma/plugin/manager.rb +12 -1
  12. data/lib/fusuma/version.rb +1 -1
  13. data/spec/helpers/config_helper.rb +20 -0
  14. data/spec/lib/config/searcher_spec.rb +97 -0
  15. data/spec/lib/config_spec.rb +112 -0
  16. data/spec/lib/custom_process_spec.rb +28 -0
  17. data/spec/lib/device_spec.rb +96 -0
  18. data/spec/lib/dummy_config.yml +31 -0
  19. data/spec/lib/fusuma_spec.rb +103 -0
  20. data/spec/lib/libinput-list-devices_iberianpig-XPS-9360.txt +181 -0
  21. data/spec/lib/libinput-list-devices_magic_trackpad.txt +51 -0
  22. data/spec/lib/libinput-list-devices_razer_razer_blade.txt +252 -0
  23. data/spec/lib/libinput-list-devices_thejinx0r.txt +361 -0
  24. data/spec/lib/libinput-list-devices_unavailable.txt +36 -0
  25. data/spec/lib/libinput_command_spec.rb +164 -0
  26. data/spec/lib/plugin/base_spec.rb +74 -0
  27. data/spec/lib/plugin/buffers/buffer_spec.rb +80 -0
  28. data/spec/lib/plugin/buffers/dummy_buffer.rb +20 -0
  29. data/spec/lib/plugin/buffers/gesture_buffer_spec.rb +172 -0
  30. data/spec/lib/plugin/detectors/detector_spec.rb +43 -0
  31. data/spec/lib/plugin/detectors/dummy_detector.rb +24 -0
  32. data/spec/lib/plugin/detectors/pinch_detector_spec.rb +119 -0
  33. data/spec/lib/plugin/detectors/rotate_detector_spec.rb +125 -0
  34. data/spec/lib/plugin/detectors/swipe_detector_spec.rb +118 -0
  35. data/spec/lib/plugin/events/event_spec.rb +30 -0
  36. data/spec/lib/plugin/events/records/gesture_record_spec.rb +22 -0
  37. data/spec/lib/plugin/events/records/record_spec.rb +31 -0
  38. data/spec/lib/plugin/events/records/text_record_spec.rb +26 -0
  39. data/spec/lib/plugin/executors/command_executor_spec.rb +57 -0
  40. data/spec/lib/plugin/executors/executor_spec.rb +160 -0
  41. data/spec/lib/plugin/filters/filter_spec.rb +92 -0
  42. data/spec/lib/plugin/filters/libinput_filter_spec.rb +120 -0
  43. data/spec/lib/plugin/inputs/input_spec.rb +70 -0
  44. data/spec/lib/plugin/inputs/libinput_command_input_spec.rb +121 -0
  45. data/spec/lib/plugin/inputs/timer_input_spec.rb +40 -0
  46. data/spec/lib/plugin/manager_spec.rb +27 -0
  47. data/spec/lib/plugin/parsers/parser_spec.rb +45 -0
  48. data/spec/spec_helper.rb +20 -0
  49. metadata +83 -42
  50. data/.github/FUNDING.yml +0 -8
  51. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -32
  52. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -17
  53. data/.github/pull_request_template.md +0 -9
  54. data/.github/stale.yml +0 -18
  55. data/.gitignore +0 -17
  56. data/.reek.yml +0 -96
  57. data/.rspec +0 -2
  58. data/.rubocop.yml +0 -43
  59. data/.rubocop_todo.yml +0 -55
  60. data/.solargraph.yml +0 -16
  61. data/.travis.yml +0 -9
  62. data/CHANGELOG.md +0 -456
  63. data/CODE_OF_CONDUCT.md +0 -74
  64. data/CONTRIBUTING.md +0 -72
  65. data/Gemfile +0 -23
  66. data/Rakefile +0 -15
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require './lib/fusuma/plugin/events/records/gesture_record'
5
+
6
+ module Fusuma
7
+ module Plugin
8
+ module Events
9
+ module Records
10
+ RSpec.describe GestureRecord do
11
+ let(:record) do
12
+ described_class.new(status: 'updating',
13
+ gesture: 'swipe',
14
+ finger: 3,
15
+ direction: direction)
16
+ end
17
+ let(:direction) { GestureRecord::Delta.new(0, 0, 1, 0) }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require './lib/fusuma/plugin/events/records/record'
5
+
6
+ module Fusuma
7
+ module Plugin
8
+ module Events
9
+ module Records
10
+ RSpec.describe Record do
11
+ class DummyRecord < Records::Record
12
+ def type
13
+ :dummy
14
+ end
15
+ end
16
+ let(:record) { described_class.new }
17
+
18
+ describe '#type' do
19
+ it { expect { record.type }.to raise_error(NotImplementedError) }
20
+
21
+ context 'override #type' do
22
+ let(:record) { DummyRecord.new }
23
+ it { expect { record.type }.not_to raise_error(NotImplementedError) }
24
+ it { expect(record.type).to eq :dummy }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require './lib/fusuma/plugin/events/records/text_record'
5
+
6
+ module Fusuma
7
+ module Plugin
8
+ module Events
9
+ module Records
10
+ RSpec.describe TextRecord do
11
+ let(:record) { described_class.new('this is dummy') }
12
+
13
+ describe '#type' do
14
+ subject { record.type }
15
+ it { is_expected.to eq :text }
16
+ end
17
+
18
+ describe '#to_s' do
19
+ subject { record.to_s }
20
+ it { is_expected.to eq 'this is dummy' }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require './lib/fusuma/plugin/executors/command_executor'
5
+ require './lib/fusuma/plugin/events/event'
6
+
7
+ module Fusuma
8
+ module Plugin
9
+ module Executors
10
+ RSpec.describe CommandExecutor do
11
+ before do
12
+ index = Config::Index.new([:dummy, 1, :direction])
13
+ record = Events::Records::IndexRecord.new(index: index)
14
+ @event = Events::Event.new(tag: 'dummy_detector', record: record)
15
+ @executor = CommandExecutor.new
16
+ end
17
+
18
+ around do |example|
19
+ ConfigHelper.load_config_yml = <<~CONFIG
20
+ dummy:
21
+ 1:
22
+ direction:
23
+ command: 'echo dummy'
24
+ interval: 1
25
+ CONFIG
26
+
27
+ example.run
28
+
29
+ Config.custom_path = nil
30
+ end
31
+
32
+ describe '#execute' do
33
+ it 'spawn' do
34
+ command = 'echo dummy'
35
+ env = {}
36
+ expect(Process).to receive(:spawn).with(env, command)
37
+ expect(Process).to receive(:detach).with(anything)
38
+ @executor.execute(@event)
39
+ end
40
+ end
41
+
42
+ describe '#executable?' do
43
+ context 'detector is matched with config file' do
44
+ it { expect(@executor.executable?(@event)).to be_truthy }
45
+ end
46
+
47
+ context 'detector is NOT matched with config file' do
48
+ before do
49
+ @event.tag = 'invalid'
50
+ end
51
+ it { expect(@executor.executable?(@event)).to be_falsey }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ require './lib/fusuma/config'
6
+ require './lib/fusuma/plugin/executors/executor'
7
+ require './lib/fusuma/plugin/detectors/detector'
8
+ require './lib/fusuma/plugin/events/event'
9
+
10
+ module Fusuma
11
+ module Plugin
12
+ module Executors
13
+ RSpec.describe Executor do
14
+ before { @executor = Executor.new }
15
+
16
+ describe '#execute' do
17
+ it do
18
+ expect { @executor.execute('dummy') }.to raise_error(NotImplementedError)
19
+ end
20
+ end
21
+
22
+ describe '#executable?' do
23
+ it do
24
+ expect { @executor.executable?('dummy') }.to raise_error(NotImplementedError)
25
+ end
26
+ end
27
+ end
28
+
29
+ class DummyExecutor < Executor
30
+ def execute(event)
31
+ index = Config::Index.new([*event.record.index.keys, :dummy])
32
+ content = Config.search(index)
33
+
34
+ # stdout
35
+ puts content if executable?(event)
36
+ end
37
+
38
+ def executable?(event)
39
+ event.tag == 'dummy'
40
+ end
41
+ end
42
+
43
+ RSpec.describe DummyExecutor do
44
+ before do
45
+ index = Config::Index.new([:dummy_gesture,
46
+ Config::Index::Key.new(:dummy_direction, skippable: true)])
47
+ record = Events::Records::IndexRecord.new(index: index)
48
+ @event = Events::Event.new(tag: 'dummy', record: record)
49
+ @executor = DummyExecutor.new
50
+ end
51
+
52
+ around do |example|
53
+ ConfigHelper.load_config_yml = <<~CONFIG
54
+ dummy_gesture:
55
+ dummy_direction:
56
+ dummy: 'echo dummy'
57
+ interval: 0.3
58
+
59
+ plugin:
60
+ executors:
61
+ dummy_executor:
62
+ dummy: dummy
63
+ CONFIG
64
+
65
+ example.run
66
+
67
+ Config.custom_path = nil
68
+ end
69
+
70
+ describe '#execute' do
71
+ it { expect { @executor.execute(@event) }.to output("echo dummy\n").to_stdout }
72
+ context 'without executable' do
73
+ before do
74
+ allow(@executor).to receive(:executable?).and_return false
75
+ end
76
+ it { expect { @executor.execute(@event) }.not_to output("echo dummy\n").to_stdout }
77
+ end
78
+ end
79
+
80
+ describe '#executable?' do
81
+ it { expect(@executor.executable?(@event)).to be_truthy }
82
+ end
83
+
84
+ describe 'interval' do
85
+ it 'should return interval from Config' do
86
+ interval_time = 0.3 * DummyExecutor::BASE_ONESHOT_INTERVAL
87
+ expect(@executor.interval(@event)).to eq interval_time
88
+ end
89
+
90
+ context 'without skippable direction' do
91
+ around do |example|
92
+ ConfigHelper.load_config_yml = <<~CONFIG
93
+ dummy_gesture:
94
+ interval: 0.1
95
+ dummy_direction:
96
+ dummy: 'echo dummy'
97
+
98
+ plugin:
99
+ executors:
100
+ dummy_executor:
101
+ dummy: dummy
102
+ CONFIG
103
+
104
+ example.run
105
+
106
+ Config.custom_path = nil
107
+ end
108
+ it 'should not return parent interval' do
109
+ expect(@executor.interval(@event)).to eq DummyExecutor::BASE_ONESHOT_INTERVAL
110
+ expect(@executor.interval(@event)).not_to eq 0.1 * DummyExecutor::BASE_ONESHOT_INTERVAL
111
+ end
112
+
113
+ context 'with Config::Searcher.skip' do
114
+ it 'should return parent interval' do
115
+ Config::Searcher.skip do
116
+ expect(@executor.interval(@event)).to eq 0.1 * DummyExecutor::BASE_ONESHOT_INTERVAL
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe 'enough_interval?' do
124
+ it 'should return true at first time' do
125
+ expect(@executor.enough_interval?(@event)).to eq true
126
+ end
127
+
128
+ context 'after update_interval' do
129
+ before do
130
+ @executor.update_interval(@event)
131
+ end
132
+ it 'should return false' do
133
+ expect(@executor.enough_interval?(@event)).to eq false
134
+ end
135
+
136
+ context 'after wait interval time' do
137
+ before do
138
+ # dummy_gesture/dummy_direction/interval => 0.3
139
+ interval_time = 0.3 * DummyExecutor::BASE_ONESHOT_INTERVAL
140
+
141
+ @event2 = Events::Event.new(
142
+ time: @event.time + interval_time,
143
+ tag: 'dummy',
144
+ record: @event.record
145
+ )
146
+ end
147
+ it 'should return true after wait' do
148
+ expect(@executor.enough_interval?(@event2)).to eq true
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ describe '#config_params' do
155
+ it { expect(@executor.config_params).to eq(dummy: 'dummy') }
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'tempfile'
5
+
6
+ require './lib/fusuma/config'
7
+ require './lib/fusuma/plugin/filters/filter'
8
+ require './lib/fusuma/plugin/events/event'
9
+
10
+ module Fusuma
11
+ module Plugin
12
+ module Filters
13
+ class DummyFilter < Filter
14
+ DEFAULT_SOURCE = 'dummy_input'
15
+
16
+ def config_param_types
17
+ {
18
+ source: String
19
+ }
20
+ end
21
+ end
22
+
23
+ RSpec.describe DummyFilter do
24
+ let(:filter) { DummyFilter.new }
25
+
26
+ describe '#source' do
27
+ subject { filter.source }
28
+
29
+ it { is_expected.to eq DummyFilter::DEFAULT_SOURCE }
30
+
31
+ context 'with config' do
32
+ around do |example|
33
+ @custom_source = 'custom_input'
34
+
35
+ ConfigHelper.load_config_yml = <<~CONFIG
36
+ plugin:
37
+ filters:
38
+ dummy_filter:
39
+ source: #{@custom_source}
40
+ CONFIG
41
+
42
+ example.run
43
+
44
+ Config.custom_path = nil
45
+ end
46
+
47
+ it { is_expected.to eq @custom_source }
48
+ end
49
+ end
50
+
51
+ describe '#filter' do
52
+ subject { filter.filter(event) }
53
+ let(:event) { Events::Event.new(tag: 'dummy_input', record: 'dummy') }
54
+
55
+ context 'when filter#keep? return false' do
56
+ before do
57
+ allow(filter).to receive(:keep?).and_return(false)
58
+ end
59
+
60
+ it { is_expected.to be nil }
61
+ end
62
+
63
+ context 'when filter#keep? return true' do
64
+ before do
65
+ allow(filter).to receive(:keep?).and_return(true)
66
+ end
67
+
68
+ it { is_expected.to be event }
69
+ end
70
+ end
71
+
72
+ describe '#config_params' do
73
+ around do |example|
74
+ ConfigHelper.load_config_yml = <<~CONFIG
75
+ plugin:
76
+ filters:
77
+ dummy_filter:
78
+ dummy: dummy
79
+ CONFIG
80
+
81
+ example.run
82
+
83
+ Config.custom_path = nil
84
+ end
85
+
86
+ subject { filter.config_params }
87
+ it { is_expected.to eq(dummy: 'dummy') }
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'tempfile'
5
+
6
+ require './lib/fusuma/config'
7
+ require './lib/fusuma/plugin/filters/libinput_device_filter'
8
+ require './lib/fusuma/plugin/events/event'
9
+
10
+ module Fusuma
11
+ module Plugin
12
+ module Filters
13
+ RSpec.describe LibinputDeviceFilter do
14
+ before do
15
+ @filter = LibinputDeviceFilter.new
16
+ end
17
+
18
+ describe '#source' do
19
+ it { expect(@filter.source).to eq LibinputDeviceFilter::DEFAULT_SOURCE }
20
+
21
+ context 'with config' do
22
+ around do |example|
23
+ @custom_source = 'custom_input'
24
+
25
+ ConfigHelper.load_config_yml = <<~CONFIG
26
+ plugin:
27
+ filters:
28
+ libinput_device_filter:
29
+ source: #{@custom_source}
30
+ CONFIG
31
+
32
+ example.run
33
+
34
+ Config.custom_path = nil
35
+ end
36
+
37
+ it { expect(@filter.source).to eq @custom_source }
38
+ end
39
+ end
40
+
41
+ describe '#filter' do
42
+ before do
43
+ @event = Events::Event.new(tag: 'libinput_command_input', record: 'dummy')
44
+ end
45
+
46
+ context 'when filter#keep? return false' do
47
+ before do
48
+ allow(@filter).to receive(:keep?).and_return(false)
49
+ end
50
+
51
+ it { expect(@filter.filter(@event)).to be nil }
52
+ end
53
+
54
+ context 'when filter#keep? return true' do
55
+ before do
56
+ allow(@filter).to receive(:keep?).and_return(true)
57
+ end
58
+
59
+ it { expect(@filter.filter(@event)).to be @event }
60
+ end
61
+ end
62
+
63
+ describe '#keep?' do
64
+ before do
65
+ device = Device.new(id: 'event18', name: 'Awesome Touchpad', available: true)
66
+ allow(Device).to receive(:all).and_return([device])
67
+ @keep_device = LibinputDeviceFilter::KeepDevice.new(name_patterns: [])
68
+ allow(@filter).to receive(:keep_device).and_return(@keep_device)
69
+ end
70
+
71
+ context 'when including record generated from touchpad' do
72
+ before do
73
+ text = ' event18 GESTURE_SWIPE_UPDATE +1.44s 4 11.23/ 1.00 (36.91/ 3.28 unaccelerated) '
74
+ @event = Events::Event.new(tag: 'libinput_command_input', record: text)
75
+ end
76
+ it 'should keep record' do
77
+ expect(@filter.keep?(@event.record)).to be true
78
+ end
79
+
80
+ context 'when including -' do
81
+ before do
82
+ text = '-event18 GESTURE_SWIPE_UPDATE +1.44s 4 11.23/ 1.00 (36.91/ 3.28 unaccelerated) '
83
+ @event = Events::Event.new(tag: 'libinput_command_input', record: text)
84
+ end
85
+ it 'should keep record' do
86
+ expect(@filter.keep?(@event.record)).to be true
87
+ end
88
+ end
89
+ end
90
+ context 'when new device is added' do
91
+ before do
92
+ text = '-event18 DEVICE_ADDED Apple Wireless Trackpad seat0 default group13 cap:pg size 132x112mm tap(dl off) left scroll-nat scroll-2fg-edge click-buttonareas-clickfing '
93
+ @event = Events::Event.new(tag: 'libinput_command_input', record: text)
94
+ end
95
+ it 'should reset KeepDevice' do
96
+ expect(@keep_device).to receive(:reset)
97
+ @filter.keep?(@event.record)
98
+ end
99
+
100
+ it 'discard DEVICE_ADDED record' do
101
+ expect(@filter.keep?(@event.record)).to be false
102
+ end
103
+
104
+ context 'when keep device is NOT matched' do
105
+ before do
106
+ @keep_device = LibinputDeviceFilter::KeepDevice.new(name_patterns: ['Microsoft Arc Mouse'])
107
+ allow(@filter).to receive(:keep_device).and_return(@keep_device)
108
+ end
109
+ it 'should NOT reset KeepDevice' do
110
+ expect(@keep_device).not_to receive(:reset)
111
+ # NOTE: @event.record is 'Apple Wireless Touchpad'
112
+ @filter.keep?(@event.record)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end