unobtainium 0.2.1 → 0.3.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.
@@ -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: