device_api-ios 1.0.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -4
- data/Gemfile.lock +25 -21
- data/device_api-ios.gemspec +3 -1
- data/lib/device_api/ios.rb +8 -1
- data/lib/device_api/ios/device.rb +73 -0
- data/lib/device_api/ios/idevice.rb +2 -1
- data/lib/device_api/ios/idevicedebug.rb +36 -0
- data/lib/device_api/ios/ideviceinstaller.rb +96 -0
- data/lib/device_api/ios/idevicename.rb +25 -0
- data/lib/device_api/ios/ideviceprovision.rb +98 -0
- data/lib/device_api/ios/ipaddress.rb +51 -0
- data/lib/device_api/ios/plistutil.rb +58 -0
- data/lib/device_api/ios/signing.rb +86 -0
- data/spec/ios_device_spec.rb +29 -82
- data/spec/ios_ideviceinstaller_spec.rb +97 -0
- data/spec/ios_ipaddress_spec.rb +35 -0
- data/spec/ios_plistutil_spec.rb +25 -0
- data/spec/ios_signing_spec.rb +62 -0
- metadata +42 -5
- data/spec/ios_idevice_spec.rb +0 -109
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44a319cbde8d0c83a2c6ce73f50a9f659381363e
|
4
|
+
data.tar.gz: 5a5251780401174266d85ce74f4c547f8736633b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62bd6649f4d4beceb65bbbae499a0daec718543557380eaefb6d6717c62369af546d50806961c720ee30ed8f22d20c8587777773ca842c2c3afe5f2e8110158b
|
7
|
+
data.tar.gz: 0654d20a10cc4b7d461c3d31253f50fa2ea090d9d3af46be195e62ef6cf9c2a7fbc0f7b4e1b1b637e2fab4985f0feb2276b165a502f0855f8c1cc5566bdc5a70
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,33 +1,37 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
|
-
remote: https://gems.cloud.bbc.co.uk/
|
4
3
|
specs:
|
5
|
-
|
6
|
-
|
7
|
-
columnize (>= 0.3.1)
|
8
|
-
debugger-linecache (~> 1.2.0)
|
9
|
-
debugger-ruby_core_source (~> 1.3.5)
|
10
|
-
debugger-linecache (1.2.0)
|
11
|
-
debugger-ruby_core_source (1.3.5)
|
12
|
-
device_api (1.0.0)
|
4
|
+
coderay (1.1.0)
|
5
|
+
device_api (1.0.1)
|
13
6
|
diff-lcs (1.2.5)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
7
|
+
ios-devices (0.2.1)
|
8
|
+
method_source (0.8.2)
|
9
|
+
ox (2.2.1)
|
10
|
+
pry (0.10.2)
|
11
|
+
coderay (~> 1.1.0)
|
12
|
+
method_source (~> 0.8.1)
|
13
|
+
slop (~> 3.4)
|
14
|
+
rspec (3.3.0)
|
15
|
+
rspec-core (~> 3.3.0)
|
16
|
+
rspec-expectations (~> 3.3.0)
|
17
|
+
rspec-mocks (~> 3.3.0)
|
18
|
+
rspec-core (3.3.2)
|
19
|
+
rspec-support (~> 3.3.0)
|
20
|
+
rspec-expectations (3.3.1)
|
21
21
|
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
-
rspec-support (~> 3.
|
23
|
-
rspec-mocks (3.
|
24
|
-
|
25
|
-
|
22
|
+
rspec-support (~> 3.3.0)
|
23
|
+
rspec-mocks (3.3.2)
|
24
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
25
|
+
rspec-support (~> 3.3.0)
|
26
|
+
rspec-support (3.3.0)
|
27
|
+
slop (3.6.0)
|
26
28
|
|
27
29
|
PLATFORMS
|
28
30
|
ruby
|
29
31
|
|
30
32
|
DEPENDENCIES
|
31
|
-
debugger
|
32
33
|
device_api (>= 1.0.0)
|
34
|
+
ios-devices
|
35
|
+
ox
|
36
|
+
pry
|
33
37
|
rspec
|
data/device_api-ios.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'device_api-ios'
|
3
|
-
s.version = '1.0.
|
3
|
+
s.version = '1.0.3'
|
4
4
|
s.date = Time.now.strftime("%Y-%m-%d")
|
5
5
|
s.summary = 'IOS Device Management API'
|
6
6
|
s.description = 'iOS implementation of DeviceAPI'
|
@@ -10,5 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.homepage = 'https://github.com/bbc/device_api-ios'
|
11
11
|
s.license = 'MIT'
|
12
12
|
s.add_runtime_dependency 'device_api', '>=1.0', '<2.0'
|
13
|
+
s.add_runtime_dependency 'ios-devices', '>=0.2'
|
14
|
+
s.add_runtime_dependency 'ox', '>=2.1.0'
|
13
15
|
s.add_development_dependency 'rspec'
|
14
16
|
end
|
data/lib/device_api/ios.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'device_api/ios/device'
|
3
3
|
require 'device_api/ios/idevice'
|
4
|
+
require 'device_api/ios/ideviceinstaller'
|
5
|
+
require 'device_api/ios/signing'
|
6
|
+
require 'device_api/ios/plistutil'
|
7
|
+
require 'device_api/ios/idevicedebug'
|
8
|
+
require 'device_api/ios/ipaddress'
|
9
|
+
require 'device_api/ios/ideviceprovision'
|
10
|
+
require 'device_api/ios/idevicename'
|
4
11
|
|
5
12
|
module DeviceAPI
|
6
13
|
module IOS
|
@@ -19,7 +26,7 @@ module DeviceAPI
|
|
19
26
|
if serial.to_s.empty?
|
20
27
|
raise DeviceAPI::BadSerialString.new("Serial was '#{ serial.nil? ? 'nil' : serial }'")
|
21
28
|
end
|
22
|
-
DeviceAPI::IOS::Device.new(serial: serial, state: '
|
29
|
+
DeviceAPI::IOS::Device.new(serial: serial, state: 'device')
|
23
30
|
end
|
24
31
|
end
|
25
32
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'device_api/device'
|
2
2
|
require 'device_api/ios/device'
|
3
3
|
require 'device_api/ios/idevice'
|
4
|
+
require 'device_api/ios/plistutil'
|
5
|
+
require 'device_api/ios/idevicename'
|
4
6
|
require 'ios/devices'
|
5
7
|
|
6
8
|
# DeviceAPI - an interface to allow for automation of devices
|
@@ -24,6 +26,12 @@ module DeviceAPI
|
|
24
26
|
}[@state]
|
25
27
|
end
|
26
28
|
|
29
|
+
# Look up device name - i.e. Bob's iPhone
|
30
|
+
# @return (String) iOS device name
|
31
|
+
def name
|
32
|
+
IDeviceName.name(serial)
|
33
|
+
end
|
34
|
+
|
27
35
|
# Look up device model using the ios-devices gem - changing 'iPad4,7' to 'iPad mini 3'
|
28
36
|
# @return (String) human readable model and version (where applicable)
|
29
37
|
def model
|
@@ -54,6 +62,64 @@ module DeviceAPI
|
|
54
62
|
IDevice.trusted?(serial)
|
55
63
|
end
|
56
64
|
|
65
|
+
# Get the app bundle ID from the specified app
|
66
|
+
# @return [String] app bundle id
|
67
|
+
def package_name(app)
|
68
|
+
app_info = Plistutil.get_bundle_id_from_app(app)
|
69
|
+
app_info['CFBundleIdentifier']
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get the app version from the specified app
|
73
|
+
# @return [String] app version
|
74
|
+
def app_version_number(app)
|
75
|
+
app_info = Plistutil.get_bundle_id_from_app(app)
|
76
|
+
app_info['CFBundleVersion']
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the IP Address from the device
|
80
|
+
# @return [String] IP Address of current device
|
81
|
+
def ip_address
|
82
|
+
IPAddress.address(serial)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get the Wifi Mac address for the current device
|
86
|
+
# @return [String] Mac address of current device
|
87
|
+
def wifi_mac_address
|
88
|
+
get_prop('WiFiAddress')
|
89
|
+
end
|
90
|
+
|
91
|
+
# Install a specified IPA
|
92
|
+
# @param [String] ipa string containing path to the IPA to install
|
93
|
+
# @return [Boolean, Exception] true when the IPA installed successfully, otherwise an error is raised
|
94
|
+
def install(ipa)
|
95
|
+
fail StandardError, 'No IPA or app specified.', caller if ipa.empty?
|
96
|
+
|
97
|
+
res = install_ipa(ipa)
|
98
|
+
|
99
|
+
fail StandardError, res, caller unless res
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
# Uninstall a specified package
|
104
|
+
# @param [String] package_name string containing name of package to uninstall
|
105
|
+
# @return [Boolean, Exception] true when the package is uninstalled successfully, otherwise an error is raised
|
106
|
+
def uninstall(package_name)
|
107
|
+
res = uninstall_package(package_name)
|
108
|
+
|
109
|
+
fail StandardError, res, caller unless res
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
# Return whether or not the device is a tablet or mobile
|
114
|
+
# @return [Symbol] :tablet or :mobile depending on device_class
|
115
|
+
def type
|
116
|
+
if device_class.downcase == 'ipad'
|
117
|
+
:tablet
|
118
|
+
else
|
119
|
+
:mobile
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
57
123
|
private
|
58
124
|
|
59
125
|
def get_prop(key)
|
@@ -63,6 +129,13 @@ module DeviceAPI
|
|
63
129
|
@props[key]
|
64
130
|
end
|
65
131
|
|
132
|
+
def install_ipa(ipa)
|
133
|
+
IDeviceInstaller.install_ipa(ipa: ipa, serial: serial)
|
134
|
+
end
|
135
|
+
|
136
|
+
def uninstall_package(package_name)
|
137
|
+
IDeviceInstaller.uninstall_package(package: package_name, serial: serial)
|
138
|
+
end
|
66
139
|
end
|
67
140
|
end
|
68
141
|
end
|
@@ -30,7 +30,8 @@ module DeviceAPI
|
|
30
30
|
# @return true if the device returns information to ideviceinfo, otherwise false
|
31
31
|
def self.trusted?(device_id)
|
32
32
|
result = execute("ideviceinfo -u '#{device_id}'")
|
33
|
-
|
33
|
+
|
34
|
+
return true if result.exit == 0 && !result.stdout.split("\n")[0].match('Usage')
|
34
35
|
false
|
35
36
|
end
|
36
37
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# DeviceAPI - an interface to allow for automation of devices
|
2
|
+
module DeviceAPI
|
3
|
+
# iOS component of DeviceAPI
|
4
|
+
module IOS
|
5
|
+
# Namespace for all methods encapsulating idevice calls
|
6
|
+
class IDeviceDebug < Execution
|
7
|
+
|
8
|
+
# idevicedebug doesn't return until the app you are attempting to run
|
9
|
+
# exits. By passing in a timeout value we can limit how long we wait
|
10
|
+
# before terminating the debug session
|
11
|
+
# @param [Hash] options options for debug running
|
12
|
+
# @option options [String] :serial serial of the device run
|
13
|
+
# @option options [String] :bundle_id ID of the app to run
|
14
|
+
# @option options [Integer] :timeout Number of seconds before the debug session should be killed
|
15
|
+
# @return [Hash] Returns the stdout of the debug session
|
16
|
+
def self.run(options = {})
|
17
|
+
serial = options[:serial]
|
18
|
+
bundle_id = options[:bundle_id]
|
19
|
+
timeout = options[:timeout] || 10
|
20
|
+
|
21
|
+
result = execute("doalarm () { perl -e 'alarm shift; exec @ARGV' \"$@\"; }; doalarm #{timeout} idevicedebug -u #{serial} -d run #{bundle_id}")
|
22
|
+
|
23
|
+
raise IDeviceDebugError.new(result.stderr) unless [0, 255, 142].include?(result.exit)
|
24
|
+
|
25
|
+
result.stdout.split("\r\n")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Error class for the IDeviceDebug class
|
30
|
+
class IDeviceDebugError < StandardError
|
31
|
+
def initialize(msg)
|
32
|
+
super(msg)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'device_api/execution'
|
2
|
+
|
3
|
+
# DeviceAPI - an interface to allow for automation of devices
|
4
|
+
module DeviceAPI
|
5
|
+
# iOS component of DeviceAPI
|
6
|
+
module IOS
|
7
|
+
# Namespace for all methods encapsulating idevice calls
|
8
|
+
class IDeviceInstaller < Execution
|
9
|
+
# Installs a given IPA to the specified device
|
10
|
+
# @param [Hash] options options for installing the app
|
11
|
+
# @option options [String] :ipa path to the IPA to install
|
12
|
+
# @option options [String] :serial serial of the target device
|
13
|
+
# @return [Boolean] true if successful, otherwise false
|
14
|
+
def self.install_ipa(options = {})
|
15
|
+
options[:action] = :install
|
16
|
+
change_package(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Uninstalls a specified package from a device
|
20
|
+
# @param [Hash] options options for uninstalling the app
|
21
|
+
# @option options [String] :package bundle ID of the package to be uninstalled
|
22
|
+
# @option options [String] :serial serial of the target device
|
23
|
+
# @return [Boolean] true if successful, otherwise false
|
24
|
+
def self.uninstall_package(options = {})
|
25
|
+
options[:action] = :uninstall
|
26
|
+
change_package(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.change_package(options = {})
|
30
|
+
package = options[:package]
|
31
|
+
ipa = options[:ipa]
|
32
|
+
serial = options[:serial]
|
33
|
+
action = options[:action]
|
34
|
+
|
35
|
+
command = nil
|
36
|
+
if action == :install
|
37
|
+
command = "ideviceinstaller -u '#{serial}' -i '#{ipa}'"
|
38
|
+
elsif action == :uninstall
|
39
|
+
command = "ideviceinstaller -u '#{serial}' -U '#{package}'"
|
40
|
+
end
|
41
|
+
|
42
|
+
raise IDeviceInstallerError.new('No action specified') if command.nil?
|
43
|
+
|
44
|
+
result = execute(command)
|
45
|
+
|
46
|
+
raise IDeviceInstallerError.new(result.stderr) if result.exit != 0
|
47
|
+
|
48
|
+
lines = result.stdout.split("\n").map { |line| line.gsub('-', '').strip }
|
49
|
+
|
50
|
+
return true if lines.last.match('Complete')
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Lists packages installed on the specified device
|
55
|
+
# @param [String] serial serial of the target device
|
56
|
+
# @return [Hash] hash containing installed packages
|
57
|
+
def self.list_installed_packages(serial)
|
58
|
+
result = execute("ideviceinstaller -u '#{serial}' -l")
|
59
|
+
|
60
|
+
raise IDeviceInstallerError.new(result.stderr) if result.exit != 0
|
61
|
+
|
62
|
+
lines = result.stdout.split("\n")
|
63
|
+
lines.shift
|
64
|
+
packages = {}
|
65
|
+
lines.each do |line|
|
66
|
+
if /(.*)\s+-\s+(.*)\s+(\d.*)/.match(line)
|
67
|
+
packages[Regexp.last_match[2]] = { package_name: Regexp.last_match[1], version: Regexp.last_match[3] }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
packages
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check to see if a package is installed
|
74
|
+
# @param [Hash] options options for checking for installed package
|
75
|
+
# @option options [String] :package package ID to check for
|
76
|
+
# @option options [String] :serial serial of the target device
|
77
|
+
# @return [Boolean] true if the package is installed, false otherwise
|
78
|
+
def self.package_installed?(options = {})
|
79
|
+
package = options[:package]
|
80
|
+
serial = options[:serial]
|
81
|
+
|
82
|
+
installed_packages = list_installed_packages(serial)
|
83
|
+
|
84
|
+
matches = installed_packages.select { |_, values| values[:package_name] == package }
|
85
|
+
return !matches.empty?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Error class for IDeviceInstaller class
|
90
|
+
class IDeviceInstallerError < StandardError
|
91
|
+
def initialize(msg)
|
92
|
+
super(msg)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# DeviceAPI - an interface to allow for automation of devices
|
2
|
+
module DeviceAPI
|
3
|
+
# iOS component of DeviceAPI
|
4
|
+
module IOS
|
5
|
+
# Namespace for all methods encapsulating idevicename calls
|
6
|
+
class IDeviceName < Execution
|
7
|
+
|
8
|
+
# Returns the device name based on the provided UUID
|
9
|
+
# @param device_id uuid of the device
|
10
|
+
# @return device name if device is connected
|
11
|
+
def self.name(device_id)
|
12
|
+
result = execute("idevicename -u #{device_id}")
|
13
|
+
return IDeviceNameError.new(result.stderr) if result.exit != 0
|
14
|
+
result.stdout.strip
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Error class for the IDeviceName class
|
19
|
+
class IDeviceNameError < StandardError
|
20
|
+
def initialize(msg)
|
21
|
+
super(msg)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# DeviceAPI - an interface to allow for automation of devices
|
2
|
+
module DeviceAPI
|
3
|
+
# iOS component of DeviceAPI
|
4
|
+
module IOS
|
5
|
+
# Namespace for all methods encapsulating ideviceprovision calls
|
6
|
+
class IDeviceProvision < Execution
|
7
|
+
# Lists all profiles on the specified device
|
8
|
+
# @param [String] serial serial of the device to check
|
9
|
+
# @return [Hash] hash of profile name and UUID
|
10
|
+
def self.list_profiles(serial)
|
11
|
+
result = execute("ideviceprovision -u #{serial} list")
|
12
|
+
|
13
|
+
raise IDeviceProvisionError.new(result.stderr) if result.exit != 0
|
14
|
+
|
15
|
+
Hash[result.stdout.split("\n").map { |a| b = a.split(' - '); [b[0], b[1]] }[1..-1]]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Checks to see if a profile is installed on the specified device
|
19
|
+
# @param [Hash] options options used for checking profiles
|
20
|
+
# @option options [String] :name name of the profile (optional when uuid provided)
|
21
|
+
# @option options [String] :uuid UUID of the profile (optional when name provided)
|
22
|
+
# @option options [String] :serial serial of the device to check
|
23
|
+
# @return [Boolean] true if the profile is installed, false otherwise
|
24
|
+
def self.has_profile?(options = {})
|
25
|
+
name = options[:name]
|
26
|
+
uuid = options[:uuid]
|
27
|
+
serial = options[:serial]
|
28
|
+
|
29
|
+
profiles = list_profiles(serial)
|
30
|
+
|
31
|
+
profiles.key?(uuid) || profiles.value?(name)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Removes the specified profile from the device
|
35
|
+
# @param [Hash] options options used for removing a profile
|
36
|
+
# @option options [String] :uuid UUID of the profile to be removed
|
37
|
+
# @option options [String] :serial serial of the device to remove the profile from
|
38
|
+
# @return [Boolean, IDeviceProvisionError] true if the profile is removed from the device, an error otherwise
|
39
|
+
def self.remove_profile(options = {})
|
40
|
+
uuid = options[:uuid]
|
41
|
+
serial = options[:serial]
|
42
|
+
|
43
|
+
return true unless has_profile?(serial: serial, uuid: uuid)
|
44
|
+
|
45
|
+
result = execute("ideviceprovision -u #{serial} remove #{uuid}")
|
46
|
+
|
47
|
+
raise IDeviceProvisionError.new(result.stderr) if result.exit != 0
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Installs the specified profile to the device
|
52
|
+
# @param [Hash] options options used for installing a profile
|
53
|
+
# @option options [String] :file path to the provisioning profile
|
54
|
+
# @option options [String] :serial serial of the device to install the profile to
|
55
|
+
# @return [Boolean, IDeviceProvisionError] true if the profile is installed, an error otherwise
|
56
|
+
def self.install_profile(options = {})
|
57
|
+
serial = options[:serial]
|
58
|
+
file = options[:file]
|
59
|
+
|
60
|
+
info = get_profile_info(file)
|
61
|
+
|
62
|
+
# Check to see if the profile has already been added to the device
|
63
|
+
return true if has_profile?(serial: serial, uuid: info['UUID'])
|
64
|
+
|
65
|
+
result = execute("ideviceprovision -u #{serial} install #{file}")
|
66
|
+
|
67
|
+
raise IDeviceProvisionError.new(result.stderr) if result.exit != 0
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
# Gets information about a provisioning profile
|
72
|
+
# @param [String] file path to the provisioning profile
|
73
|
+
# @return [Hash] hash containing provisioning profile information
|
74
|
+
def self.get_profile_info(file)
|
75
|
+
result = execute("ideviceprovision dump #{file}")
|
76
|
+
|
77
|
+
raise IDeviceProvisionError.new(result.stderr) if result.exit != 0
|
78
|
+
|
79
|
+
lines = result.stdout.split("\n")
|
80
|
+
|
81
|
+
info = {}
|
82
|
+
lines.each do |l|
|
83
|
+
if /(.*):\s+(.*)/.match(l)
|
84
|
+
info[Regexp.last_match[1]] = Regexp.last_match[2]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
info
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Provisioning error class
|
92
|
+
class IDeviceProvisionError < StandardError
|
93
|
+
def initialize(msg)
|
94
|
+
super(msg)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'device_api/ios/idevicedebug'
|
2
|
+
require 'device_api/ios/ideviceinstaller'
|
3
|
+
|
4
|
+
# DeviceAPI - an interface to allow for automation of devices
|
5
|
+
module DeviceAPI
|
6
|
+
# iOS component of DeviceAPI
|
7
|
+
module IOS
|
8
|
+
# Namespace for all methods encapsulating idevice calls
|
9
|
+
class IPAddress < Execution
|
10
|
+
|
11
|
+
# Package name for the IP Address app
|
12
|
+
def self.ipaddress_bundle_id
|
13
|
+
'uk.co.bbc.titan.IPAddress'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Check to see if the IPAddress app is installed
|
17
|
+
# @param [String] serial serial of the target device
|
18
|
+
# @return [Boolean] returns true if the app is installed
|
19
|
+
def self.installed?(serial)
|
20
|
+
if DeviceAPI::IOS::IDeviceInstaller.package_installed?( serial: serial, package: ipaddress_bundle_id )
|
21
|
+
return true
|
22
|
+
else
|
23
|
+
raise IPAddressError.new('IP Address package not installed: Please see https://github.com/bbc/ios-test-helper')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get the IP Address from the installed app
|
28
|
+
# @param [String] serial serial of the target device
|
29
|
+
# @return [String] IP Address if found
|
30
|
+
def self.address(serial)
|
31
|
+
installed?(serial)
|
32
|
+
result = IDeviceDebug.run(serial: serial, bundle_id: ipaddress_bundle_id )
|
33
|
+
|
34
|
+
ip_address = nil
|
35
|
+
result.each do |line|
|
36
|
+
if /"en0\/ipv4" = "(.*)"/.match(line)
|
37
|
+
ip_address = Regexp.last_match[1]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
ip_address
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Error class for the IPAddress class
|
45
|
+
class IPAddressError < StandardError
|
46
|
+
def initialize(msg)
|
47
|
+
super(msg)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'device_api/execution'
|
2
|
+
require 'device_api/ios/signing'
|
3
|
+
require 'ox'
|
4
|
+
|
5
|
+
# DeviceAPI - an interface to allow for automation of devices
|
6
|
+
module DeviceAPI
|
7
|
+
# iOS component of DeviceAPI
|
8
|
+
module IOS
|
9
|
+
# Namespace for all methods encapsulating plistutil calls
|
10
|
+
class Plistutil < Execution
|
11
|
+
|
12
|
+
# Check to ensure that plistutil is available
|
13
|
+
# @return [Boolean] true if plistutil is available, false otherwise
|
14
|
+
def self.plistutil_available?
|
15
|
+
result = execute('which plistutil')
|
16
|
+
result.exit == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
# Gets properties from the IPA and returns them in a hash
|
20
|
+
# @param [String] path path to the IPA/App
|
21
|
+
# @return [Hash] list of properties from the app
|
22
|
+
def self.get_bundle_id_from_app(path)
|
23
|
+
path = Signing.unpack_ipa(path) if Signing.is_ipa?(path)
|
24
|
+
get_bundle_id_from_plist("#{path}/Info.plist")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Gets properties from the IPA and returns them in a hash
|
28
|
+
# @param [String] plist path to the plist
|
29
|
+
# @return [Hash] list of properties from the app
|
30
|
+
def self.get_bundle_id_from_plist(plist)
|
31
|
+
raise PlistutilCommandError.new('plistutil not found') unless plistutil_available?
|
32
|
+
result = execute("plistutil -i #{plist}")
|
33
|
+
raise PlistutilCommandError.new(result.stderr) if result.exit != 0
|
34
|
+
info = Ox.parse(result.stdout)
|
35
|
+
nodes = info.locate('*/dict')
|
36
|
+
values = {}
|
37
|
+
last_key = nil
|
38
|
+
nodes.each do |node|
|
39
|
+
node.nodes.each do |child|
|
40
|
+
if child.value == 'key'
|
41
|
+
last_key = child.nodes.first
|
42
|
+
elsif child.value == 'string'
|
43
|
+
values[last_key] = child.nodes.first
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
values
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# plistutil error class
|
52
|
+
class PlistutilCommandError < StandardError
|
53
|
+
def initialize(msg)
|
54
|
+
super(msg)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'device_api/execution'
|
2
|
+
|
3
|
+
# DeviceAPI - an interface to allow for automation of devices
|
4
|
+
module DeviceAPI
|
5
|
+
# iOS component of DeviceAPI
|
6
|
+
module IOS
|
7
|
+
# Namespace for all methods encapsulating idevice calls
|
8
|
+
class Signing < Execution
|
9
|
+
|
10
|
+
# Check to see if the path is an IPA
|
11
|
+
def self.is_ipa?(path)
|
12
|
+
return true if (File.extname path).downcase == '.ipa'
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.unpack_ipa(path)
|
17
|
+
folder = File.dirname(path)
|
18
|
+
target = (File.basename path, (File.extname path))
|
19
|
+
|
20
|
+
# Check to see if the target has already been unzipped
|
21
|
+
return Dir["#{folder}/#{target}/Payload/*.app"].first if File.exists? ("#{folder}/#{target}/Payload/")
|
22
|
+
|
23
|
+
result = execute("unzip '#{path}' -d '#{folder}/#{target}'")
|
24
|
+
raise SigningCommandError.new(result.stderr) if result.exit != 0
|
25
|
+
|
26
|
+
Dir["#{folder}/#{target}/Payload/*.app"].first
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.is_app_signed?(app_path)
|
30
|
+
app_path = unpack_ipa(app_path) if is_ipa?(app_path)
|
31
|
+
result = execute("codesign -d -vvvv '#{app_path}'")
|
32
|
+
|
33
|
+
if result.exit != 0
|
34
|
+
return false if /is not signed/.match(result.stderr)
|
35
|
+
raise SigningCommandError.new(result.stderr)
|
36
|
+
end
|
37
|
+
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.sign_app(options = {})
|
42
|
+
cert = options[:cert]
|
43
|
+
entitlements = options[:entitlements]
|
44
|
+
app = options[:app]
|
45
|
+
|
46
|
+
result = execute("codesign --force --sign #{cert} --entitlements #{entitlements} '#{app}'")
|
47
|
+
|
48
|
+
raise SigningCommandError.new(result.stderr) if result.exit != 0
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.get_signing_certs
|
53
|
+
result = execute('security find-identity -p codesigning -v')
|
54
|
+
|
55
|
+
raise SigningCommandError.new(result.stderr) if result.exit != 0
|
56
|
+
|
57
|
+
certs = []
|
58
|
+
result.stdout.split("\n").each do |line|
|
59
|
+
if /\)\s*(\S*)\s*"(.*)"/.match(line)
|
60
|
+
certs << { id: Regexp.last_match[1], name: Regexp.last_match[2] }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
certs
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.get_entitlements(app_path)
|
67
|
+
app_path = unpack_ipa(app_path) if is_ipa?(app_path)
|
68
|
+
result = execute("codesign -d --entitlements - #{app_path}")
|
69
|
+
|
70
|
+
require 'pry'
|
71
|
+
binding.pry
|
72
|
+
if result.exit != 0
|
73
|
+
raise SigningCommandError.new(result.stderr)
|
74
|
+
end
|
75
|
+
return result.stdout
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Signing error class
|
80
|
+
class SigningCommandError < StandardError
|
81
|
+
def initialize(msg)
|
82
|
+
super(msg)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/spec/ios_device_spec.rb
CHANGED
@@ -1,104 +1,51 @@
|
|
1
|
-
$LOAD_PATH.unshift( './lib/' )
|
2
|
-
|
3
1
|
require 'device_api/execution'
|
4
2
|
require 'device_api/ios/idevice'
|
5
3
|
require 'device_api/ios/device'
|
6
|
-
require '
|
4
|
+
require 'device_api/ios'
|
7
5
|
|
8
6
|
include RSpec
|
9
7
|
|
10
|
-
ProcessStatusStub = Struct.new(:exitstatus)
|
11
|
-
$STATUS_ZERO = ProcessStatusStub.new(0)
|
12
|
-
|
13
8
|
describe DeviceAPI::IOS do
|
14
9
|
|
15
|
-
before(:all) do
|
16
|
-
|
17
|
-
config_file = '../../config.yml'
|
18
|
-
CONFIG = YAML.load_file(File.expand_path(config_file, __FILE__))
|
19
|
-
@bundle_id = CONFIG["bundle_id"]
|
20
|
-
@app_path = File.expand_path(CONFIG["app_path"])
|
21
|
-
@arr_devices = DeviceAPI::IOS::IDevice.get_list_of_devices
|
22
|
-
keys = @arr_devices[0].keys
|
23
|
-
@test_device_uuid = keys[0]
|
24
|
-
@app_name = CONFIG["app_name"]
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
10
|
describe ".model" do
|
29
11
|
|
30
|
-
|
31
|
-
|
12
|
+
it 'returns the model of the attached device' do
|
13
|
+
device = DeviceAPI::IOS.device('123456')
|
14
|
+
expect(device.model).to eq('Unknown iOS device')
|
32
15
|
end
|
33
16
|
|
34
|
-
it
|
35
|
-
|
36
|
-
|
17
|
+
it 'returns the correct result when a device is trusted' do
|
18
|
+
device = DeviceAPI::IOS.device('123456')
|
19
|
+
output = <<end
|
20
|
+
ActivationState: Activated
|
21
|
+
ActivationStateAcknowledged: true
|
22
|
+
end
|
23
|
+
allow(Open3).to receive(:capture3) {
|
24
|
+
[output, '', (Struct.new(:exitstatus)).new(0)]
|
25
|
+
}
|
26
|
+
expect(device.trusted?).to eq(true)
|
37
27
|
end
|
38
28
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
describe ".install" do
|
45
|
-
|
46
|
-
before(:each) do
|
47
|
-
|
48
|
-
result = DeviceAPI::Execution.execute("ideviceinstaller -u #{@test_device_uuid} -l")
|
49
|
-
if(result.stdout.include? @bundle_id and result.stderr=="")
|
50
|
-
DeviceAPI::Execution.execute("ideviceinstaller -u '#{@test_device_uuid}' -U #{@bundle_id}")
|
51
|
-
end
|
52
|
-
|
53
|
-
@ios_device = DeviceAPI::IOS::Device.new(:serial=>@test_device_uuid,:state => "ok",:type => "Device")
|
54
|
-
|
29
|
+
it 'returns the correct result when a device is not trusted' do
|
30
|
+
device = DeviceAPI::IOS.device('123456')
|
31
|
+
expect(device.trusted?).to eq(false)
|
55
32
|
end
|
56
33
|
|
57
|
-
it
|
58
|
-
|
34
|
+
it 'returns device state' do
|
35
|
+
device = DeviceAPI::IOS.device('123456')
|
36
|
+
expect(device.status).to eq(:ok)
|
59
37
|
end
|
60
|
-
|
61
38
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
if(!app_names.include? @app_name)
|
73
|
-
DeviceAPI::Execution.execute("ideviceinstaller -u '#{@test_device_uuid}' -i #{@app_path}")
|
74
|
-
@local_bundle_id = (@ios_device.bundle_id_list)[@app_name]
|
75
|
-
else
|
76
|
-
@local_bundle_id = app_bundle_list[@app_name]
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
it "Uninstalls the app from ios device" do
|
81
|
-
res = @ios_device.uninstall(@local_bundle_id)
|
82
|
-
expect( res ).to eq :success
|
83
|
-
|
39
|
+
it 'returns the device name' do
|
40
|
+
output = <<-end
|
41
|
+
Test Device
|
42
|
+
end
|
43
|
+
allow(Open3).to receive(:capture3) {
|
44
|
+
[output, '', (Struct.new(:exitstatus)).new(0)]
|
45
|
+
}
|
46
|
+
device = DeviceAPI::IOS.device('123456')
|
47
|
+
expect(device.name).to eq('Test Device')
|
84
48
|
end
|
85
|
-
|
86
|
-
|
87
49
|
end
|
88
|
-
|
89
|
-
describe ".get_props" do
|
90
|
-
before(:each) do
|
91
|
-
@ios_device = DeviceAPI::IOS::Device.new(:serial=>@test_device_uuid,:state => "ok",:type => "Device")
|
92
|
-
end
|
93
|
-
|
94
|
-
it "Returns a hash of name value pair properties" do
|
95
|
-
props = @ios_device.get_props
|
96
|
-
expect( props ).to be_a Hash
|
97
|
-
expect( props['ActivationState']).to eq('Activated')
|
98
|
-
end
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
|
103
50
|
end
|
104
51
|
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'device_api/ios/ideviceinstaller'
|
2
|
+
|
3
|
+
describe DeviceAPI::IOS::IDeviceInstaller do
|
4
|
+
describe '.list_installed_packages' do
|
5
|
+
it 'returns a list of installed apps' do
|
6
|
+
output = <<end
|
7
|
+
Total: 2 apps
|
8
|
+
uk.co.bbc.titan.IPAddress - IPAddress 1
|
9
|
+
uk.co.bbc.iplayer - BBC iPlayer 4.10.0.196
|
10
|
+
end
|
11
|
+
allow(Open3).to receive(:capture3) do
|
12
|
+
[output, '', (Struct.new(:exitstatus)).new(0)]
|
13
|
+
end
|
14
|
+
apps = DeviceAPI::IOS::IDeviceInstaller.list_installed_packages('123456')
|
15
|
+
expect(apps.count).to eq(2)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.install_ipa' do
|
20
|
+
it 'installs an app' do
|
21
|
+
output = <<end
|
22
|
+
Installing 'uk.co.mediaat.iplayer'
|
23
|
+
- CreatingStagingDirectory (5%)
|
24
|
+
- ExtractingPackage (15%)
|
25
|
+
- InspectingPackage (20%)
|
26
|
+
- TakingInstallLock (20%)
|
27
|
+
- PreflightingApplication (30%)
|
28
|
+
- InstallingEmbeddedProfile (30%)
|
29
|
+
- VerifyingApplication (40%)
|
30
|
+
- CreatingContainer (50%)
|
31
|
+
- InstallingApplication (60%)
|
32
|
+
- PostflightingApplication (70%)
|
33
|
+
- SandboxingApplication (80%)
|
34
|
+
- GeneratingApplicationMap (90%)
|
35
|
+
- Complete
|
36
|
+
end
|
37
|
+
allow(Open3).to receive(:capture3) do
|
38
|
+
[output, '', (Struct.new(:exitstatus)).new(0)]
|
39
|
+
end
|
40
|
+
result = DeviceAPI::IOS::IDeviceInstaller.install_ipa(serial: '123456', ipa: 'iplayer.ipa' )
|
41
|
+
expect(result).to eq(true)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '.uninstall_app' do
|
46
|
+
it 'uninstalls an app' do
|
47
|
+
output = <<end
|
48
|
+
Uninstalling 'uk.co.bbc.iplayer'
|
49
|
+
- RemovingApplication (50%)
|
50
|
+
- GeneratingApplicationMap (90%)
|
51
|
+
- Complete
|
52
|
+
end
|
53
|
+
allow(Open3).to receive(:capture3) do
|
54
|
+
[output, '', (Struct.new(:exitstatus)).new(0)]
|
55
|
+
end
|
56
|
+
result = DeviceAPI::IOS::IDeviceInstaller.uninstall_package(package: 'uk.co.bbc.iplayer', serial: '123456')
|
57
|
+
expect(result).to eq(true)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'fails to remove an app that is not installed' do
|
61
|
+
output = <<end
|
62
|
+
Uninstalling 'uk.co.bbc.iplaye'
|
63
|
+
- RemovingApplication (50%)
|
64
|
+
- GeneratingApplicationMap (90%)
|
65
|
+
- Error occurred: APIInternalError
|
66
|
+
end
|
67
|
+
allow(Open3).to receive(:capture3) do
|
68
|
+
[output, '', (Struct.new(:exitstatus)).new(0)]
|
69
|
+
end
|
70
|
+
result = DeviceAPI::IOS::IDeviceInstaller.uninstall_package(package: 'uk.co.bbc.iplaye', serial: '123456')
|
71
|
+
expect(result).to eq(false)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '.package_installed?' do
|
76
|
+
before(:each) do
|
77
|
+
output = <<end
|
78
|
+
Total: 2 apps
|
79
|
+
uk.co.bbc.titan.IPAddress - IPAddress 1
|
80
|
+
uk.co.bbc.iplayer - BBC iPlayer 4.10.0.196
|
81
|
+
end
|
82
|
+
allow(Open3).to receive(:capture3) do
|
83
|
+
[output, '', (Struct.new(:exitstatus)).new(0)]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'identifies when a package is installed' do
|
88
|
+
result = DeviceAPI::IOS::IDeviceInstaller.package_installed?(package: 'uk.co.bbc.iplayer', serial: '123456')
|
89
|
+
expect(result).to eq(true)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'identifies when a package is not installed' do
|
93
|
+
result = DeviceAPI::IOS::IDeviceInstaller.package_installed?(package: 'uk.co.bbc.sport', serial: '123456')
|
94
|
+
expect(result).to eq(false)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'device_api/execution'
|
2
|
+
require 'device_api/ios/ipaddress'
|
3
|
+
|
4
|
+
describe DeviceAPI::IOS::IPAddress do
|
5
|
+
|
6
|
+
describe '.address' do
|
7
|
+
it 'gets the correct IP Address' do
|
8
|
+
|
9
|
+
apps = <<end
|
10
|
+
Total: 2 apps\r
|
11
|
+
uk.co.bbc.titan.IPAddress - IPAddress 1\r
|
12
|
+
uk.co.bbc.iplayer - BBC iPlayer 4.10.0.196\r
|
13
|
+
end
|
14
|
+
|
15
|
+
output = <<end
|
16
|
+
2015-07-06 09:27:33.670 IPAddress[801:299377] addresses: {
|
17
|
+
"awdl0/ipv6" = "fe80::dce9:ge0f:fee7:aaad";
|
18
|
+
"en0/ipv4" = "10.10.1.80";
|
19
|
+
"en0/ipv6" = "fe80::147c:1496:fce9:ed2";
|
20
|
+
"lo0/ipv4" = "127.0.0.1";
|
21
|
+
"lo0/ipv6" = "fe80::1";
|
22
|
+
"pdp_ip0/ipv4" = "10.7.195.3";
|
23
|
+
}
|
24
|
+
2015-07-06 09:27:33.672 IPAddress[801:299377] 10.10.1.80
|
25
|
+
end
|
26
|
+
allow(Open3).to receive(:capture3).and_return(
|
27
|
+
[apps, '', (Struct.new(:exitstatus)).new(0)],
|
28
|
+
[output, '', (Struct.new(:exitstatus)).new(0)]
|
29
|
+
)
|
30
|
+
|
31
|
+
ip_address = DeviceAPI::IOS::IPAddress.address('123456')
|
32
|
+
expect(ip_address).to eq('10.10.1.80')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'device_api/ios/plistutil'
|
2
|
+
|
3
|
+
describe DeviceAPI::IOS::Plistutil do
|
4
|
+
describe ".get_app_bundle_id" do
|
5
|
+
it "returns the correct app bundle" do
|
6
|
+
|
7
|
+
xml = <<end
|
8
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
9
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
10
|
+
<plist version="1.0">
|
11
|
+
<dict>
|
12
|
+
<key>CFBundleIdentifier</key>
|
13
|
+
<string>com.example.apple-samplecode.UICatalog</string>
|
14
|
+
</dict>
|
15
|
+
</plist>
|
16
|
+
end
|
17
|
+
|
18
|
+
allow(Open3).to receive(:capture3) {
|
19
|
+
[xml, '', (Struct.new(:exitstatus)).new(0)]
|
20
|
+
}
|
21
|
+
bundle = DeviceAPI::IOS::Plistutil.get_bundle_id_from_plist('Info.plist')
|
22
|
+
expect(bundle['CFBundleIdentifier']).to eq('com.example.apple-samplecode.UICatalog')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
$LOAD_PATH.unshift( './lib/' )
|
2
|
+
|
3
|
+
require 'device_api/execution'
|
4
|
+
require 'device_api/ios/signing'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
include RSpec
|
8
|
+
|
9
|
+
describe DeviceAPI::IOS::Signing do
|
10
|
+
|
11
|
+
before(:all) do
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".is_ipa?" do
|
16
|
+
it "correctly identifies an IPA" do
|
17
|
+
expect(DeviceAPI::IOS::Signing.is_ipa?('/path/to/ipa.ipa')).to be(true)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "correctly identifies a file that isn't an IPA" do
|
21
|
+
expect(DeviceAPI::IOS::Signing.is_ipa?('/path/to/app.app')).to be(false)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe ".get_signing_certs" do
|
26
|
+
it "returns an Array of Hashes containing iOS Signing Certificates" do
|
27
|
+
expect(DeviceAPI::IOS::Signing.get_signing_certs).to be_kind_of(Array)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns an Array of Hashes containing correct certificates" do
|
31
|
+
out = <<-eos
|
32
|
+
1) 43ED4FA24518B1F72EE4FB3E6F7476E886A8E5D0 "iPhone Developer: Test Developer (ABC1234567)"
|
33
|
+
2) 289765876A0FB55327F8F3C2A3D4FA3F1A484CFB "iPhone Developer: Test Developer (ABC1234526)"
|
34
|
+
3) 132128763516473546816751267AFA217036217B "iPhone Developer: Test Developer (ABC1235343)"
|
35
|
+
3 valid identities found
|
36
|
+
eos
|
37
|
+
allow(Open3).to receive(:capture3) {
|
38
|
+
[out, '', (Struct.new(:exitstatus)).new(0)]
|
39
|
+
}
|
40
|
+
|
41
|
+
expect(DeviceAPI::IOS::Signing.get_signing_certs.count).to eq(3)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "entitlements" do
|
46
|
+
it 'returns a list of entitlements for an app' do
|
47
|
+
out = <<-eos
|
48
|
+
<dict>
|
49
|
+
<key>application-identifier</key>
|
50
|
+
<string>uk.co.bbc.test</string>
|
51
|
+
<key>get-task-allow</key>
|
52
|
+
<false/>
|
53
|
+
</dict>
|
54
|
+
eos
|
55
|
+
allow(Open3).to receive(:capture3) {
|
56
|
+
[out, '', (Struct.new(:exitstatus)).new(0)]
|
57
|
+
}
|
58
|
+
|
59
|
+
expect(DeviceAPI::IOS::Signing.get_entitlements('test.ipa')).to eq(true)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: device_api-ios
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- BBC
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2016-02-03 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: device_api
|
@@ -32,6 +32,34 @@ dependencies:
|
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '2.0'
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: ios-devices
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0.2'
|
42
|
+
type: :runtime
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0.2'
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: ox
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 2.1.0
|
56
|
+
type: :runtime
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.1.0
|
35
63
|
- !ruby/object:Gem::Dependency
|
36
64
|
name: rspec
|
37
65
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,8 +90,18 @@ files:
|
|
62
90
|
- lib/device_api/ios.rb
|
63
91
|
- lib/device_api/ios/device.rb
|
64
92
|
- lib/device_api/ios/idevice.rb
|
93
|
+
- lib/device_api/ios/idevicedebug.rb
|
94
|
+
- lib/device_api/ios/ideviceinstaller.rb
|
95
|
+
- lib/device_api/ios/idevicename.rb
|
96
|
+
- lib/device_api/ios/ideviceprovision.rb
|
97
|
+
- lib/device_api/ios/ipaddress.rb
|
98
|
+
- lib/device_api/ios/plistutil.rb
|
99
|
+
- lib/device_api/ios/signing.rb
|
65
100
|
- spec/ios_device_spec.rb
|
66
|
-
- spec/
|
101
|
+
- spec/ios_ideviceinstaller_spec.rb
|
102
|
+
- spec/ios_ipaddress_spec.rb
|
103
|
+
- spec/ios_plistutil_spec.rb
|
104
|
+
- spec/ios_signing_spec.rb
|
67
105
|
homepage: https://github.com/bbc/device_api-ios
|
68
106
|
licenses:
|
69
107
|
- MIT
|
@@ -84,9 +122,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
122
|
version: '0'
|
85
123
|
requirements: []
|
86
124
|
rubyforge_project:
|
87
|
-
rubygems_version: 2.4.
|
125
|
+
rubygems_version: 2.4.8
|
88
126
|
signing_key:
|
89
127
|
specification_version: 4
|
90
128
|
summary: IOS Device Management API
|
91
129
|
test_files: []
|
92
|
-
has_rdoc:
|
data/spec/ios_idevice_spec.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift( './lib/' )
|
2
|
-
|
3
|
-
require 'device_api/execution'
|
4
|
-
require 'device_api/ios/idevice'
|
5
|
-
require 'yaml'
|
6
|
-
|
7
|
-
include RSpec
|
8
|
-
|
9
|
-
ProcessStatusStub = Struct.new(:exitstatus)
|
10
|
-
$STATUS_ZERO = ProcessStatusStub.new(0)
|
11
|
-
|
12
|
-
describe DeviceAPI::IOS::IDevice do
|
13
|
-
|
14
|
-
before(:all) do
|
15
|
-
|
16
|
-
config_file = '../../config.yml'
|
17
|
-
CONFIG = YAML.load_file(File.expand_path(config_file, __FILE__))
|
18
|
-
@bundle_id = CONFIG["bundle_id"]
|
19
|
-
@app_path = File.expand_path(CONFIG["app_path"])
|
20
|
-
@arr_devices = DeviceAPI::IOS::IDevice.get_list_of_devices
|
21
|
-
keys = @arr_devices[0].keys
|
22
|
-
@test_device_uuid = keys[0]
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
describe ".get_list_of_devices" do
|
27
|
-
|
28
|
-
before(:each) do
|
29
|
-
@arr_devices = DeviceAPI::IOS::IDevice.get_list_of_devices
|
30
|
-
end
|
31
|
-
|
32
|
-
it "returns an Array of connected ios devices" do
|
33
|
-
expect(@arr_devices.class).to eq(Array)
|
34
|
-
end
|
35
|
-
|
36
|
-
it "returns an Array of Hash of connected ios devices - When atleast one devices is connected" do
|
37
|
-
expect(@arr_devices[0].class).to eq(Hash)
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
describe ".get_state" do
|
43
|
-
|
44
|
-
it "returns state of a device based on the device - When atleast one device is connected" do
|
45
|
-
expect( DeviceAPI::IOS::IDevice.get_state(@test_device_uuid)).to eq("Activated")
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
describe ".install_app" do
|
52
|
-
|
53
|
-
before(:each) do
|
54
|
-
|
55
|
-
result = DeviceAPI::Execution.execute("ideviceinstaller -u #{@test_device_uuid} -l")
|
56
|
-
if(result.stdout.include? @bundle_id and result.stderr=="")
|
57
|
-
DeviceAPI::Execution.execute("ideviceinstaller -u '#{@test_device_uuid}' -U #{@bundle_id}")
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
it "returns successfully installed message once ios app is installed on a device" do
|
63
|
-
expect( DeviceAPI::IOS::IDevice.install_app(@test_device_uuid,@app_path)).to eq true
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
describe ".launch_app" do
|
70
|
-
|
71
|
-
before(:each) do
|
72
|
-
|
73
|
-
result = DeviceAPI::Execution.execute("ideviceinstaller -u #{@test_device_uuid} -l")
|
74
|
-
if(result.stdout.include? @bundle_id and result.stderr=="")
|
75
|
-
DeviceAPI::Execution.execute("ideviceinstaller -u '#{@test_device_uuid}' -U #{@bundle_id}")
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
it "Launches the app on the ios device" do
|
81
|
-
props = DeviceAPI::IOS::IDevice.launch_app(@test_device_uuid,@app_path)
|
82
|
-
expect( props ).to eq true
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
describe ".get_props" do
|
90
|
-
|
91
|
-
it "Returns a hash of name value pair properties" do
|
92
|
-
props = DeviceAPI::IOS::IDevice.get_props(@test_device_uuid)
|
93
|
-
expect( props ).to be_a Hash
|
94
|
-
expect( props['ActivationState']).to eq('Activated')
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
describe ".bundle_id_list" do
|
100
|
-
|
101
|
-
it "Returns a hash of name value pair of application name and bundle_id" do
|
102
|
-
list = DeviceAPI::IOS::IDevice.bundle_id_list(@test_device_uuid)
|
103
|
-
expect(list).to be_a Hash
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
|