x_do 0.1.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.
@@ -0,0 +1,126 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe XDo::FFILib do
4
+ describe 'XDoSearch#from_options' do
5
+ describe 'with no flag' do
6
+ let(:search) do
7
+ XDo::FFILib::XDoSearch.from_options
8
+ end
9
+
10
+ it 'should set flags to 0' do
11
+ search[:searchmask].should == 0
12
+ end
13
+
14
+ it 'should set require to AND' do
15
+ search[:require].should == 1
16
+ end
17
+
18
+ it 'should set maximum depth to -1' do
19
+ search[:max_depth].should == -1
20
+ end
21
+ end
22
+
23
+ describe 'with a set maximum depth' do
24
+ let(:search) do
25
+ XDo::FFILib::XDoSearch.from_options :depth => 42
26
+ end
27
+
28
+ it 'should not set any flags' do
29
+ search[:searchmask].should == 0
30
+ end
31
+
32
+ it 'should set field correctly' do
33
+ search[:max_depth].should == 42
34
+ end
35
+ end
36
+
37
+ describe 'with require set to :any' do
38
+ let(:search) do
39
+ XDo::FFILib::XDoSearch.from_options :require => :any
40
+ end
41
+
42
+ it 'should not set any flags' do
43
+ search[:searchmask].should == 0
44
+ end
45
+
46
+ it 'should set require correctly' do
47
+ search[:require].should == 0
48
+ end
49
+ end
50
+
51
+ describe 'with 1 boolean flag' do
52
+ let(:search) do
53
+ XDo::FFILib::XDoSearch.from_options :visible => true
54
+ end
55
+
56
+ it 'should set flags correctly' do
57
+ search[:searchmask].should == XDo::FFILib::Consts::SEARCH_ONLYVISIBLE
58
+ end
59
+
60
+ it 'should set fields correctly' do
61
+ search[:only_visible].should == 1
62
+ end
63
+ end
64
+
65
+ describe 'with 1 int flag' do
66
+ let(:search) do
67
+ XDo::FFILib::XDoSearch.from_options :screen => 42
68
+ end
69
+
70
+ it 'should set flags correctly' do
71
+ search[:searchmask].should == XDo::FFILib::Consts::SEARCH_SCREEN
72
+ end
73
+
74
+ it 'should set field correctly' do
75
+ search[:screen].should == 42
76
+ end
77
+ end
78
+
79
+ describe 'with 1 string flag' do
80
+ let(:search) do
81
+ XDo::FFILib::XDoSearch.from_options :class => 'Normal'
82
+ end
83
+
84
+ it 'should set flags correctly' do
85
+ search[:searchmask].should == XDo::FFILib::Consts::SEARCH_CLASS
86
+ end
87
+
88
+ it 'should set fields correctly' do
89
+ search[:winclass].get_string(0).should == 'Normal'
90
+ end
91
+ end
92
+
93
+ describe 'with 2 string flags' do
94
+ let(:search) do
95
+ XDo::FFILib::XDoSearch.from_options :title => 'Terminal',
96
+ :name => 'gnome-terminal'
97
+ end
98
+
99
+ it 'should set flags correctly' do
100
+ search[:searchmask].should == XDo::FFILib::Consts::SEARCH_NAME |
101
+ XDo::FFILib::Consts::SEARCH_TITLE
102
+ end
103
+
104
+ it 'should set fields correctly' do
105
+ search[:winname].get_string(0).should == 'gnome-terminal'
106
+ search[:title].get_string(0).should == 'Terminal'
107
+ end
108
+ end
109
+
110
+ describe 'with mixed flags' do
111
+ let(:search) do
112
+ XDo::FFILib::XDoSearch.from_options :class_name => 'rbx', :pid => 42
113
+ end
114
+
115
+ it 'should set flags correctly' do
116
+ search[:searchmask].should == XDo::FFILib::Consts::SEARCH_CLASSNAME |
117
+ XDo::FFILib::Consts::SEARCH_PID
118
+ end
119
+
120
+ it 'should set fields correctly' do
121
+ search[:winclassname].get_string(0).should == 'rbx'
122
+ search[:pid].should == 42
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe XDo::Keyboard do
4
+ let(:xdo) { XDo.new }
5
+ let(:keyboard) { xdo.keyboard }
6
+
7
+ describe 'after pressing Alt+Tab' do
8
+ before do
9
+ @old_active_window = xdo.focused_window
10
+ keyboard.type_keysequence 'Alt_L+Tab'
11
+ sleep 0.1
12
+ end
13
+ after do
14
+ keyboard.type_keysequence 'Alt_L+Tab'
15
+ sleep 0.1
16
+ end
17
+
18
+ it 'should switch the focused window' do
19
+ xdo.focused_window.should_not == @old_active_window
20
+ end
21
+ end
22
+
23
+ describe 'after typing injected' do
24
+ before do
25
+ keyboard.type_string "injected\n"
26
+ end
27
+
28
+ it 'should reflect the string in gets' do
29
+ $stdin.gets.should == "injected\n"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe XDo::Mouse do
4
+ let(:xdo) { XDo.new }
5
+ let(:mouse) { xdo.mouse }
6
+
7
+ describe 'location' do
8
+ let(:location) { mouse.location }
9
+
10
+ it 'should have 3 coordinates' do
11
+ location.should have(3).coordinates
12
+ end
13
+
14
+ it 'should have non-negative coordinates' do
15
+ location[0].should >= 0
16
+ location[1].should >= 0
17
+ location[2].should >= 0
18
+ end
19
+
20
+ describe 'after moving' do
21
+ let(:new_location) { [location[0] + 20, location[1] + 20, location[2]] }
22
+ before do
23
+ mouse.move(*new_location)
24
+ mouse.wait_for_move_from location[0], location[1]
25
+ end
26
+ after { mouse.move(*location) }
27
+
28
+ it 'should change to the move arguments' do
29
+ mouse.location.should == new_location
30
+ end
31
+
32
+ it 'should not block wait_for_move_to' do
33
+ lambda {
34
+ mouse.wait_for_move_to new_location[0], new_location[1]
35
+ }.should_not raise_error
36
+ end
37
+ end
38
+
39
+ describe 'after relative moving' do
40
+ let(:new_location) { [location[0] + 20, location[1] + 20, location[2]] }
41
+ before do
42
+ new_location
43
+ mouse.move_relative 20, 20
44
+ mouse.wait_for_move_from location[0], location[1]
45
+ end
46
+ after { mouse.move(*location) }
47
+
48
+ it 'should change to the move arguments' do
49
+ mouse.location.should == new_location
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,141 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe XDo::Window do
4
+ let(:xdo) { XDo.new }
5
+ let(:all) { xdo.find_windows }
6
+
7
+ describe 'last window' do
8
+ let(:window) { all.last }
9
+
10
+ it 'should == another instance with the same window id' do
11
+ window.should == XDo::Window.new(xdo, window._window)
12
+ end
13
+
14
+ it 'should hash to the same value as another instance with the same window id' do
15
+ window.hash.should == XDo::Window.new(xdo, window._window).hash
16
+ end
17
+
18
+ it 'should not == an instance with a different id' do
19
+ window.should_not == XDo::Window.new(xdo, window._window + 1)
20
+ end
21
+
22
+ it 'should not == a string' do
23
+ window.should_not == "window"
24
+ end
25
+
26
+ it 'should not == its id' do
27
+ window.should_not == window._window
28
+ end
29
+ end
30
+
31
+ describe 'active window' do
32
+ let(:window) { xdo.active_window }
33
+ it 'should have a non-zero pid' do
34
+ window.pid.should_not == 0
35
+ end
36
+
37
+ describe 'location' do
38
+ let(:location) { window.location }
39
+
40
+ it 'should have 2 coordinates' do
41
+ location.should have(2).coordinates
42
+ end
43
+
44
+ it 'should have non-negative coordinates' do
45
+ location.first.should >= 0
46
+ location.last.should >= 0
47
+ end
48
+
49
+ describe 'after moving' do
50
+ let(:new_location) { [location.first + 200, location.last + 200] }
51
+ before { window.move(*new_location) }
52
+ after { window.move(*location) }
53
+
54
+ it 'should change to the move arguments' do
55
+ window.location.should == new_location
56
+ end
57
+ end
58
+ end
59
+
60
+ describe 'size' do
61
+ let(:size) { window.size }
62
+
63
+ it 'should have 2 dimensions' do
64
+ size.should have(2).dimensions
65
+ end
66
+
67
+ it 'should have positive dimensions' do
68
+ size.first.should > 0
69
+ size.last.should > 0
70
+ end
71
+
72
+ describe 'after resizing' do
73
+ let(:new_size) { [size.first + 100, size.last + 100] }
74
+
75
+ before { window.resize(*new_size) }
76
+ after { window.resize(*size) }
77
+
78
+ it 'should change to approximately the resize arguments' do
79
+ window.size.first.should be_within(10).of(new_size.first)
80
+ window.size.last.should be_within(50).of(new_size.last)
81
+ end
82
+ end
83
+ end
84
+
85
+ describe 'after mouse move in window center' do
86
+ before do
87
+ @old_mouse_location = xdo.mouse.location
88
+ size = window.size
89
+ middle = [size.first / 2, size.last / 2]
90
+ window.move_mouse(*middle)
91
+ end
92
+ after do
93
+ xdo.mouse.move(*@old_mouse_location)
94
+ end
95
+
96
+ it 'should have moved the mouse cursor' do
97
+ xdo.mouse.location.should_not == @old_mouse_location
98
+ end
99
+
100
+ describe 'after right click' do
101
+ before do
102
+ @old_window_count = xdo.find_windows.length
103
+ window.click_mouse 3
104
+ sleep 0.1
105
+ end
106
+
107
+ after do
108
+ xdo.mouse.move_relative -20, -20
109
+ xdo.mouse.click 1
110
+ sleep 0.1
111
+ end
112
+
113
+ it 'should have popped up a context menu' do
114
+ xdo.find_windows.length.should_not == @old_window_count
115
+ end
116
+
117
+ describe 'after left click outside menu' do
118
+ before do
119
+ xdo.mouse.move_relative -20, -20
120
+ xdo.mouse.click 3
121
+ sleep 0.1
122
+ end
123
+
124
+ it 'should have dismissed context menu' do
125
+ xdo.find_windows.length.should == @old_window_count
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'after typing injected in the window' do
132
+ before do
133
+ window.type_string "injected\n"
134
+ end
135
+
136
+ it 'should reflect the string in gets' do
137
+ $stdin.gets.should == "injected\n"
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,108 @@
1
+ require 'ffi'
2
+ require 'ffi/tools/const_generator'
3
+ require 'ffi/tools/struct_generator'
4
+ require 'set'
5
+
6
+ desc 'Regenerate ffi_autogen.rb'
7
+ task :ffi_header do
8
+ XDo::Tasks.generate_ffi_header
9
+ end
10
+
11
+ # :nodoc: namespace
12
+ module XDo
13
+
14
+ module Tasks
15
+ def self.resolve_constants(pattern, format)
16
+ if FFI::Platform.mac?
17
+ header_path = '/System/Library/Frameworks/PCSC.framework/Headers/'
18
+ else
19
+ header_path = '/usr/include/'
20
+ end
21
+ headers = Dir.glob(header_path + 'xdo*.h').map { |f| File.basename f }
22
+
23
+ consts = Set.new
24
+ headers.each do |header|
25
+ contents = File.read("#{header_path}#{header}")
26
+ contents.each_line do |line|
27
+ tokens = line.split
28
+ next unless tokens[0] == '#define'
29
+ next if tokens[1].index '('
30
+ consts << tokens[1] if pattern =~ tokens[1]
31
+ end
32
+ contents.scan /enum\s*\{([^}]*)\}/ do |match|
33
+ match[0].split(',').each do |enum_item|
34
+ enum_name = enum_item.split('=').first.strip
35
+ consts << enum_name if pattern =~ enum_name
36
+ end
37
+ end
38
+ end
39
+
40
+ const_gen = FFI::ConstGenerator.new(nil,
41
+ :cppflags => "-w -I#{header_path}") do |g|
42
+ headers.each { |header| g.include header }
43
+ consts.each { |const| g.const const, format }
44
+ end
45
+
46
+ const_gen.constants
47
+ end
48
+
49
+ def self.output_constants(constants, f)
50
+ constants.each do |name, const|
51
+ value = const.to_ruby
52
+ value += 'nil' if value.strip[-1] == ?=
53
+ f.write " #{value}\n"
54
+ end
55
+ end
56
+
57
+ def self.output_enum(enum_name, constants, name_regexp, f)
58
+ f.write " #{enum_name} = enum [\n"
59
+
60
+ re = Regexp.new('^' + name_regexp + '$')
61
+ constants.each do |name, const|
62
+ ruby_name = re.match(name)[1]
63
+ f.write " :#{ruby_name.downcase}, Consts::#{name},\n"
64
+ end
65
+
66
+ f.write " ]\n"
67
+ end
68
+
69
+ def self.generate_ffi_header
70
+ File.open('lib/x_do/ffi_autogen.rb', 'wb') do |f|
71
+ f.write "# Automatically generated by tasks/ffi_codegen.rb\n\n"
72
+
73
+ # Return codes.
74
+ stat_consts = resolve_constants(/^XDO_.*$/, '%d')
75
+ # Search flags.
76
+ search_consts = resolve_constants(/^SEARCH_/, '0x%04X')
77
+ # Window sizing flags.
78
+ sizing_consts = resolve_constants(/^SIZE_/, '%d')
79
+ # Search direction.
80
+ direction_consts = resolve_constants(/^XDO_FIND_/, '%d')
81
+
82
+ f.write "# :nodoc: namespace\n"
83
+ f.write "class XDo\n\n"
84
+ f.write "# :nodoc: namespace\n"
85
+ f.write "module FFILib\n"
86
+
87
+ f.write " # Constant values extracted from headers.\n"
88
+ f.write " module Consts\n"
89
+ output_constants stat_consts, f
90
+ output_constants search_consts, f
91
+ output_constants sizing_consts, f
92
+ output_constants direction_consts, f
93
+ f.write " end # module XDo::FFILib::Consts\n\n"
94
+
95
+ f.write " # Status returned by libxdo functions.\n"
96
+ output_enum 'Status', stat_consts, 'XDO_(.*)', f
97
+ f.write "\n"
98
+
99
+ f.write " # Search directions.\n"
100
+ output_enum 'Direction', direction_consts, 'XDO_FIND_(.*)', f
101
+ f.write "\n"
102
+
103
+ f.write "end # namespace XDo::FFILib\n"
104
+ f.write "end # namespace XDo\n"
105
+ end
106
+ end
107
+ end # namespace XDo::Tasks
108
+ end # namespace XDo