unobtainium 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,9 @@
7
7
  # All rights reserved.
8
8
  #
9
9
  module Unobtainium
10
- module Drivers
10
+ # @api private
11
+ # Contains support code
12
+ module Support
11
13
  ##
12
14
  # Utility code shared by driver implementations
13
15
  module Utility
@@ -17,20 +19,25 @@ module Unobtainium
17
19
  # where keys are the normalized label, and the value is an array of
18
20
  # aliases:
19
21
  #
22
+ # ```ruby
20
23
  # LABELS = {
21
24
  # foo: [:alias1, :alias2],
22
25
  # bar: [],
23
26
  # }.freeze
27
+ # ```
24
28
  #
25
29
  # Empty aliases means that there are no aliases for this label.
30
+ #
31
+ # @param label [String, Symbol] the driver label to normalize
26
32
  def normalize_label(label)
33
+ sym_label = label.to_sym
27
34
  self::LABELS.each do |normalized, aliases|
28
- if label == normalized or aliases.include?(label)
35
+ if sym_label == normalized or aliases.include?(sym_label)
29
36
  return normalized
30
37
  end
31
38
  end
32
39
  return nil
33
40
  end
34
41
  end # module Utility
35
- end # module Drivers
42
+ end # module Support
36
43
  end # module Unobtainium
@@ -7,5 +7,6 @@
7
7
  # All rights reserved.
8
8
  #
9
9
  module Unobtainium
10
- VERSION = "0.2.1".freeze
10
+ # The current release version
11
+ VERSION = "0.3.0".freeze
11
12
  end
@@ -20,11 +20,12 @@ module Unobtainium
20
20
  ##
21
21
  # Modules can have class methods, too.
22
22
  module ClassMethods
23
- # Configuration related
23
+ # Set the config file path.
24
24
  def config_file=(name)
25
25
  @config_file = name
26
26
  end
27
27
 
28
+ # @return [String] the config file path, defaulting to 'config/config.yml'
28
29
  def config_file
29
30
  return @config_file || "config/config.yml"
30
31
  end
@@ -32,7 +33,7 @@ module Unobtainium
32
33
  extend ClassMethods
33
34
 
34
35
  ##
35
- # Return the global configuration, loaded from :config_file
36
+ # Return the global configuration, loaded from `World#config_file`
36
37
  def config
37
38
  return ::Unobtainium::Runtime.instance.store_with_if(:config) do
38
39
  ::Unobtainium::Config.load_config(::Unobtainium::World.config_file)
@@ -40,6 +41,8 @@ module Unobtainium
40
41
  end
41
42
 
42
43
  ##
44
+ # (see Driver#create)
45
+ #
43
46
  # Returns a driver instance with the given options. If no options are
44
47
  # provided, options from the global configuration are used.
45
48
  def driver(label = nil, options = nil)
@@ -67,7 +70,7 @@ module Unobtainium
67
70
 
68
71
  # The driver may modify the options; if so, we should let it do that
69
72
  # here. That way our key (below) is based on the expanded options.
70
- label, options = ::Unobtainium::Driver.sanitize_options(label, options)
73
+ label, options, _ = ::Unobtainium::Driver.resolve_options(label, options)
71
74
 
72
75
  # Create a key for the label and options. This should always
73
76
  # return the same key for the same label and options.
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+ require_relative '../lib/unobtainium/support/port_scanner'
3
+
4
+ describe ::Unobtainium::Support::PortScanner do
5
+ let(:tester) { Class.new { extend ::Unobtainium::Support::PortScanner } }
6
+
7
+ # This Socket#connect mock finds port 1234 or 4321 open
8
+ def connect_mock(_, addr)
9
+ port, = Socket.unpack_sockaddr_in(addr)
10
+ if port == 1234 or port == 4321
11
+ return 0
12
+ end
13
+ raise Errno::ECONNREFUSED
14
+ end
15
+
16
+ describe "port_open?" do
17
+ it "detects an open port correctly" do
18
+ allow_any_instance_of(Socket).to receive(:connect).and_return(0)
19
+ expect(tester.port_open?('localhost', 1234)).to be_truthy
20
+ end
21
+
22
+ it "detects a closed port correctly" do
23
+ allow_any_instance_of(Socket).to receive(:connect).and_raise(
24
+ Errno::ECONNREFUSED)
25
+ expect(tester.port_open?('localhost', 1234)).to be_falsy
26
+ end
27
+
28
+ it "handles a single domain parameter" do
29
+ allow_any_instance_of(Socket).to receive(:connect).and_raise(
30
+ Errno::ECONNREFUSED)
31
+ expect(tester.port_open?('localhost', 1234, :INET)).to be_falsy
32
+ end
33
+
34
+ it "handles many domain parameters" do
35
+ allow_any_instance_of(Socket).to receive(:connect).and_raise(
36
+ Errno::ECONNREFUSED)
37
+ expect(tester.port_open?('localhost', 1234, [:INET, :INET6])).to be_falsy
38
+ end
39
+
40
+ it "rejects bad domain parameters" do
41
+ expect do
42
+ tester.port_open?('localhost', 1234, :FOO)
43
+ end.to raise_error(ArgumentError)
44
+ end
45
+ end
46
+
47
+ describe "scan" do
48
+ it "aborts on bad parameters" do
49
+ expect { tester.scan }.to raise_error(ArgumentError)
50
+ expect { tester.scan('localhost') }.to raise_error(ArgumentError)
51
+ expect { tester.scan('localhost', "foo") }.to raise_error(ArgumentError)
52
+ expect { tester.scan('localhost', :sym) }.to raise_error(ArgumentError)
53
+ end
54
+
55
+ it "finds an open port in a range" do
56
+ allow_any_instance_of(Socket).to receive(:connect) do |sock, addr|
57
+ connect_mock(sock, addr)
58
+ end
59
+
60
+ expect(tester.scan('localhost', 1230..1240)).to eql [1234]
61
+ end
62
+
63
+ it "finds an open port in an array" do
64
+ allow_any_instance_of(Socket).to receive(:connect) do |sock, addr|
65
+ connect_mock(sock, addr)
66
+ end
67
+
68
+ expect(tester.scan('localhost', [1233, 1234, 1235])).to eql [1234]
69
+ end
70
+
71
+ it "doesn't find an open port in a range" do
72
+ allow_any_instance_of(Socket).to receive(:connect) do |sock, addr|
73
+ connect_mock(sock, addr)
74
+ end
75
+
76
+ expect(tester.scan('localhost', 1240..1250)).to eql []
77
+ end
78
+
79
+ it "doesn't find an open port in an array" do
80
+ allow_any_instance_of(Socket).to receive(:connect) do |sock, addr|
81
+ connect_mock(sock, addr)
82
+ end
83
+
84
+ expect(tester.scan('localhost', [1230, 1231])).to eql []
85
+ end
86
+
87
+ it "finds an open port in mixed arguments" do
88
+ allow_any_instance_of(Socket).to receive(:connect) do |sock, addr|
89
+ connect_mock(sock, addr)
90
+ end
91
+
92
+ # Match in first argument
93
+ expect(tester.scan('localhost', 1234, [1, 2], 3..4)).to eql [1234]
94
+ expect(tester.scan('localhost', 1230..1240, 3, [1, 2])).to eql [1234]
95
+ expect(tester.scan('localhost', [1, 1234], 3..4, 5)).to eql [1234]
96
+
97
+ # Match in second argument
98
+ expect(tester.scan('localhost', 1, [1, 1234], 3..4)).to eql [1234]
99
+ expect(tester.scan('localhost', 1..2, 1234, [1, 2])).to eql [1234]
100
+ expect(tester.scan('localhost', [1, 2], 1230..1240, 5)).to eql [1234]
101
+ end
102
+
103
+ it "can abort after the first find" do
104
+ allow_any_instance_of(Socket).to receive(:connect) do |sock, addr|
105
+ connect_mock(sock, addr)
106
+ end
107
+
108
+ expect(tester.scan('localhost', 1230..4330, amount: :first)).to eql [1234]
109
+ end
110
+
111
+ it "can scan for closed/available ports" do
112
+ allow_any_instance_of(Socket).to receive(:connect) do |sock, addr|
113
+ connect_mock(sock, addr)
114
+ end
115
+
116
+ expect(tester.scan('localhost', 1233..1234, for: :closed)).to eql [1233]
117
+ end
118
+
119
+ it "rejects bad amounts" do
120
+ expect do
121
+ tester.scan('localhost', 1230..4330, amount: :foo)
122
+ end.to raise_error(ArgumentError)
123
+ end
124
+
125
+ it "rejects bad for" do
126
+ expect do
127
+ tester.scan('localhost', 1230..4330, for: :foo)
128
+ end.to raise_error(ArgumentError)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require_relative '../lib/unobtainium/support/runner'
3
+
4
+ describe ::Unobtainium::Support::Runner do
5
+ it "refuses to initialize without ID" do
6
+ expect do
7
+ ::Unobtainium::Support::Runner.new
8
+ end.to raise_error(ArgumentError)
9
+ end
10
+
11
+ it "refuses to initialize without command" do
12
+ expect do
13
+ ::Unobtainium::Support::Runner.new("foo")
14
+ end.to raise_error(ArgumentError)
15
+ end
16
+
17
+ it "runs a shell command" do
18
+ runner = ::Unobtainium::Support::Runner.new("foo", %w(ls -l))
19
+ expect(runner.pid).to be_nil
20
+ runner.start
21
+ expect(runner.pid).not_to be_nil
22
+ expect(runner.pid).to be > 0
23
+ runner.wait
24
+ expect(runner.pid).to be_nil
25
+ end
26
+
27
+ it "captures output" do
28
+ runner = ::Unobtainium::Support::Runner.new("foo", %w(ls -l))
29
+ runner.start
30
+ runner.wait
31
+ expect(runner.stdout).not_to be_nil
32
+ expect(runner.stderr).not_to be_nil
33
+
34
+ # Read stdout
35
+ out = runner.stdout.read
36
+ expect(out).not_to be_empty
37
+ end
38
+
39
+ it "can be killed" do
40
+ runner = ::Unobtainium::Support::Runner.new("foo", %w(sleep 30))
41
+ runner.start
42
+ expect(runner.pid).not_to be_nil
43
+ runner.kill
44
+ expect(runner.pid).to be_nil
45
+ end
46
+
47
+ it "verifies #signal arguments" do
48
+ runner = ::Unobtainium::Support::Runner.new("foo", %w(sleep 30))
49
+ expect { runner.signal("KILL", scope: :foo) }.to raise_error(RuntimeError)
50
+ runner.start
51
+ expect { runner.signal("KILL", scope: :foo) }.to raise_error(ArgumentError)
52
+ runner.signal("KILL", scope: :self)
53
+ end
54
+
55
+ it "errors for invalid commands" do
56
+ runner = ::Unobtainium::Support::Runner.new("foo", "no_shell_command")
57
+ expect { runner.start }.to raise_error(Errno::ENOENT)
58
+ end
59
+
60
+ it "refuses to run the command twice without ending it first" do
61
+ runner = ::Unobtainium::Support::Runner.new("foo", %w(ls -l))
62
+ expect { runner.start }.not_to raise_error(RuntimeError)
63
+ expect { runner.start }.to raise_error(RuntimeError)
64
+ runner.wait
65
+ end
66
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ require_relative '../lib/unobtainium/support/util'
3
+
4
+ class Tester
5
+ LABELS = {
6
+ noalias: [],
7
+ aliases: [:foo, :bar],
8
+ conflict: [:bar]
9
+ }.freeze
10
+
11
+ extend ::Unobtainium::Support::Utility
12
+ end # class Tester
13
+
14
+ describe ::Unobtainium::Support::Utility do
15
+ it "returns nil for a lable that can't be matched" do
16
+ expect(Tester.normalize_label("nomatch")).to be_nil
17
+ expect(Tester.normalize_label(:nomatch)).to be_nil
18
+ end
19
+
20
+ it "normalizes a string label" do
21
+ expect(Tester.normalize_label("noalias")).to eql :noalias
22
+ end
23
+
24
+ it "normalizes an alias" do
25
+ expect(Tester.normalize_label("foo")).to eql :aliases
26
+ end
27
+
28
+ it "returns the first match on alias conflicts" do
29
+ expect(Tester.normalize_label("bar")).to eql :aliases
30
+ end
31
+ end
data/unobtainium.gemspec CHANGED
@@ -39,13 +39,21 @@ Gem::Specification.new do |spec|
39
39
 
40
40
  spec.required_ruby_version = '>= 2.0'
41
41
 
42
- spec.requirements = "Either or all of 'selenium-webdriver', 'appium_lib'"
42
+ spec.requirements = "Either or all of 'selenium-webdriver', 'appium_lib', "\
43
+ "'phantomjs'"
43
44
 
44
45
  spec.add_development_dependency "bundler", "~> 1.11"
45
46
  spec.add_development_dependency "rubocop", "~> 0.39"
46
47
  spec.add_development_dependency "rake", "~> 11.1"
47
48
  spec.add_development_dependency "rspec", "~> 3.4"
48
49
  spec.add_development_dependency "simplecov", "~> 0.11"
50
+ spec.add_development_dependency "yard", "~> 0.8"
51
+ spec.add_development_dependency "appium_lib"
52
+ spec.add_development_dependency "selenium-webdriver"
53
+ spec.add_development_dependency "phantomjs"
54
+ spec.add_development_dependency "cucumber"
55
+
56
+ spec.add_dependency "sys-proctable", "~> 1.0"
49
57
  end
50
58
  # rubocop:enable Style/SpaceAroundOperators
51
59
  # rubocop:enable Style/UnneededPercentQ, Style/ExtraSpacing
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unobtainium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Finkhaeuser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-13 00:00:00.000000000 Z
11
+ date: 2016-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,90 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.11'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.8'
97
+ - !ruby/object:Gem::Dependency
98
+ name: appium_lib
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: selenium-webdriver
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: phantomjs
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: cucumber
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: sys-proctable
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '1.0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '1.0'
83
167
  description: "\n Unobtainium wraps Selenium and Appium in a simple driver abstraction
84
168
  so that\n test code can more easily cover desktop browsers, mobile browsers and
85
169
  mobile\n apps.\n\n Some additional useful functionality for the maintenance
@@ -99,23 +183,23 @@ files:
99
183
  - LICENSE
100
184
  - README.md
101
185
  - Rakefile
102
- - cuke/.gitignore
103
- - cuke/Gemfile
104
- - cuke/Gemfile.lock
105
- - cuke/README.md
106
- - cuke/config/config.yml
107
- - cuke/features/step_definitions/steps.rb
108
- - cuke/features/support/env.rb
109
- - cuke/features/world.feature
186
+ - config/config.yml
187
+ - docs/CONFIGURATION.md
188
+ - features/step_definitions/steps.rb
189
+ - features/support/env.rb
190
+ - features/world.feature
110
191
  - lib/unobtainium.rb
111
192
  - lib/unobtainium/config.rb
112
193
  - lib/unobtainium/driver.rb
113
194
  - lib/unobtainium/drivers/appium.rb
195
+ - lib/unobtainium/drivers/phantom.rb
114
196
  - lib/unobtainium/drivers/selenium.rb
115
- - lib/unobtainium/drivers/support/util.rb
116
197
  - lib/unobtainium/pathed_hash.rb
117
198
  - lib/unobtainium/recursive_merge.rb
118
199
  - lib/unobtainium/runtime.rb
200
+ - lib/unobtainium/support/port_scanner.rb
201
+ - lib/unobtainium/support/runner.rb
202
+ - lib/unobtainium/support/util.rb
119
203
  - lib/unobtainium/version.rb
120
204
  - lib/unobtainium/world.rb
121
205
  - spec/config_spec.rb
@@ -134,8 +218,11 @@ files:
134
218
  - spec/driver_spec.rb
135
219
  - spec/mock_driver.rb
136
220
  - spec/pathed_hash_spec.rb
221
+ - spec/port_scanner_spec.rb
222
+ - spec/runner_spec.rb
137
223
  - spec/runtime_spec.rb
138
224
  - spec/spec_helper.rb
225
+ - spec/utility_spec.rb
139
226
  - spec/world_spec.rb
140
227
  - unobtainium.gemspec
141
228
  homepage: https://github.com/jfinkhaeuser/unobtainium
@@ -157,13 +244,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
244
  - !ruby/object:Gem::Version
158
245
  version: '0'
159
246
  requirements:
160
- - Either or all of 'selenium-webdriver', 'appium_lib'
247
+ - Either or all of 'selenium-webdriver', 'appium_lib', 'phantomjs'
161
248
  rubyforge_project:
162
249
  rubygems_version: 2.4.5.1
163
250
  signing_key:
164
251
  specification_version: 4
165
252
  summary: 'Obtain the unobtainable: test code covering multiple platforms'
166
253
  test_files:
254
+ - features/step_definitions/steps.rb
255
+ - features/support/env.rb
256
+ - features/world.feature
167
257
  - spec/config_spec.rb
168
258
  - spec/data/array.yaml
169
259
  - spec/data/arraymerge-local.yaml
@@ -180,7 +270,10 @@ test_files:
180
270
  - spec/driver_spec.rb
181
271
  - spec/mock_driver.rb
182
272
  - spec/pathed_hash_spec.rb
273
+ - spec/port_scanner_spec.rb
274
+ - spec/runner_spec.rb
183
275
  - spec/runtime_spec.rb
184
276
  - spec/spec_helper.rb
277
+ - spec/utility_spec.rb
185
278
  - spec/world_spec.rb
186
279
  has_rdoc: