luffa 1.0.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.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +39 -0
- data/LICENSE +204 -0
- data/README.md +7 -0
- data/VERSIONING.md +16 -0
- data/bin/luffa +17 -0
- data/lib/luffa.rb +12 -0
- data/lib/luffa/cli/cli.rb +65 -0
- data/lib/luffa/environment.rb +31 -0
- data/lib/luffa/gem.rb +35 -0
- data/lib/luffa/ios/ideviceinstaller.rb +116 -0
- data/lib/luffa/ios/simulator.rb +36 -0
- data/lib/luffa/ios/xcode.rb +93 -0
- data/lib/luffa/logging.rb +21 -0
- data/lib/luffa/patches/awesome_print.rb +16 -0
- data/lib/luffa/patches/kernel.rb +19 -0
- data/lib/luffa/retry.rb +13 -0
- data/lib/luffa/unix_command.rb +43 -0
- data/lib/luffa/version.rb +166 -0
- data/lib/luffa/with_debugging.rb +13 -0
- data/script/ci/com.apple.dt.instruments.process.analysis.plist +34 -0
- data/script/ci/com.apple.dt.instruments.process.kill.plist +34 -0
- metadata +323 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Luffa
|
4
|
+
class Simulator
|
5
|
+
def core_simulator_home_dir
|
6
|
+
@core_simulator_home_dir ||= File.expand_path('~/Library/Developer/CoreSimulator')
|
7
|
+
end
|
8
|
+
|
9
|
+
def core_simulator_device_dir(sim_udid=nil)
|
10
|
+
if sim_udid.nil?
|
11
|
+
@core_simulator_device_dir ||= File.expand_path(File.join(core_simulator_home_dir, 'Devices'))
|
12
|
+
else
|
13
|
+
File.expand_path(File.join(core_simulator_device_dir, sim_udid))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def core_simulator_device_containers_dir(sim_udid)
|
18
|
+
File.expand_path(File.join(core_simulator_device_dir(sim_udid), 'Containers'))
|
19
|
+
end
|
20
|
+
|
21
|
+
def core_simulator_for_xcode_version(idiom, form_factor, xcode_version)
|
22
|
+
if xcode_version < Luffa::Version.new('6.1')
|
23
|
+
ios_version = '8.0'
|
24
|
+
elsif xcode_version < Luffa::Version.new('6.2')
|
25
|
+
ios_version = '8.1'
|
26
|
+
elsif xcode_version < Luffa::Version.new('6.3')
|
27
|
+
ios_version = '8.2'
|
28
|
+
elsif xcode_version >= Luffa::Version.new('6.3')
|
29
|
+
ios_version = '8.3'
|
30
|
+
else
|
31
|
+
raise "Unsupported Xcode version: #{xcode_version}"
|
32
|
+
end
|
33
|
+
"#{idiom} #{form_factor} (#{ios_version} Simulator)"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Luffa
|
2
|
+
class XcodeInstall
|
3
|
+
attr :version
|
4
|
+
attr :path
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
Luffa::Xcode.with_developer_dir(path) do
|
8
|
+
@version = lambda {
|
9
|
+
xcode_build_output = `xcrun xcodebuild -version`.split(/\s/)[1]
|
10
|
+
Luffa::Version.new(xcode_build_output)
|
11
|
+
}.call
|
12
|
+
|
13
|
+
@path = path
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def version_string
|
18
|
+
version.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def == (other)
|
22
|
+
other.path == path && other.version == version
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Xcode
|
27
|
+
|
28
|
+
def self.with_developer_dir(developer_dir, &block)
|
29
|
+
original_developer_dir = Luffa::Environment.developer_dir
|
30
|
+
stripped = developer_dir.strip
|
31
|
+
begin
|
32
|
+
ENV['DEVELOPER_DIR'] = stripped
|
33
|
+
block.call
|
34
|
+
ensure
|
35
|
+
ENV['DEVELOPER_DIR'] = original_developer_dir
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.with_xcode_install(xcode_install, &block)
|
40
|
+
self.with_developer_dir(xcode_install.path, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.ios_version_incompatible_with_xcode_version?(ios_version, xcode_version)
|
44
|
+
[(ios_version >= Luffa::Version.new('8.0') && xcode_version < Luffa::Version.new('6.0')),
|
45
|
+
(ios_version >= Luffa::Version.new('8.1') && xcode_version < Luffa::Version.new('6.1')),
|
46
|
+
(ios_version >= Luffa::Version.new('8.2') && xcode_version < Luffa::Version.new('6.2')),
|
47
|
+
(ios_version >= Luffa::Version.new('8.3') && xcode_version < Luffa::Version.new('6.3'))].any?
|
48
|
+
end
|
49
|
+
|
50
|
+
def xcode_installs
|
51
|
+
@xcode_installs ||= lambda do
|
52
|
+
min_xcode_version = Luffa::Version.new('5.1.1')
|
53
|
+
active_xcode = Luffa::Xcode.new.active_xcode
|
54
|
+
xcodes = [active_xcode]
|
55
|
+
Dir.glob('/Xcode/*/*.app/Contents/Developer').each do |path|
|
56
|
+
xcode_version = path[/(\d\.\d(\.\d)?)/, 0]
|
57
|
+
if Luffa::Version.new(xcode_version) >= min_xcode_version
|
58
|
+
install = Luffa::XcodeInstall.new(path)
|
59
|
+
unless install == active_xcode
|
60
|
+
xcodes << install
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
xcodes
|
65
|
+
end.call
|
66
|
+
end
|
67
|
+
|
68
|
+
# The developer dir at the time the tests start.
|
69
|
+
#
|
70
|
+
# To change the active Xcode:
|
71
|
+
#
|
72
|
+
# $ DEVELOPER_DIR=/path/to/Xcode.app/Contents/Developer be rake spec
|
73
|
+
def active_xcode_dir
|
74
|
+
@active_xcode_dir ||= lambda {
|
75
|
+
env = Luffa::Environment.developer_dir
|
76
|
+
if env
|
77
|
+
env
|
78
|
+
else
|
79
|
+
# fall back to xcode-select
|
80
|
+
xcode_select_path
|
81
|
+
end
|
82
|
+
}.call
|
83
|
+
end
|
84
|
+
|
85
|
+
def xcode_select_path
|
86
|
+
@xcode_select_dir ||= `xcode-select --print-path`.strip
|
87
|
+
end
|
88
|
+
|
89
|
+
def active_xcode
|
90
|
+
@active_xcode ||= Luffa::XcodeInstall.new(active_xcode_dir)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Luffa
|
2
|
+
def self.log_unix_cmd(msg)
|
3
|
+
puts "\033[36mEXEC: #{msg}\033[0m" if msg
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.log_pass(msg)
|
7
|
+
puts "\033[32mPASS: #{msg}\033[0m" if msg
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.log_warn(msg)
|
11
|
+
puts "\033[34mWARN: #{msg}\033[0m"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.log_fail(msg)
|
15
|
+
puts "\033[31mFAIL: #{msg}\033[0m" if msg
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.log_info(msg)
|
19
|
+
puts "\033[46mINFO: #{msg}\033[0m" if msg
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
|
3
|
+
# monkey patch for AwesomePrint + objects that implement '=='
|
4
|
+
module AwesomePrint
|
5
|
+
class Formatter
|
6
|
+
def awesome_self(object, type)
|
7
|
+
if @options[:raw] && object.instance_variables.any?
|
8
|
+
awesome_object(object)
|
9
|
+
elsif object.respond_to?(:to_hash)
|
10
|
+
awesome_hash(object.to_hash)
|
11
|
+
else
|
12
|
+
colorize(object.inspect.to_s, type)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Kernel
|
2
|
+
def capture_stdout
|
3
|
+
out = StringIO.new
|
4
|
+
$stdout = out
|
5
|
+
yield
|
6
|
+
return out
|
7
|
+
ensure
|
8
|
+
$stdout = STDOUT
|
9
|
+
end
|
10
|
+
|
11
|
+
def capture_stderr
|
12
|
+
out = StringIO.new
|
13
|
+
$stderr = out
|
14
|
+
yield
|
15
|
+
return out
|
16
|
+
ensure
|
17
|
+
$stderr = STDERR
|
18
|
+
end
|
19
|
+
end
|
data/lib/luffa/retry.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Luffa
|
2
|
+
def self.unix_command(cmd, opts={})
|
3
|
+
default_opts = {:pass_msg => nil,
|
4
|
+
:fail_msg => nil,
|
5
|
+
:exit_on_nonzero_status => true,
|
6
|
+
:env_vars => {},
|
7
|
+
:log_cmd => true,
|
8
|
+
:obscure_fields => []}
|
9
|
+
merged_opts = default_opts.merge(opts)
|
10
|
+
|
11
|
+
obscure_fields = merged_opts[:obscure_fields]
|
12
|
+
|
13
|
+
if not obscure_fields.empty? and merged_opts[:log_cmd]
|
14
|
+
obscured = cmd.split(' ').map do |token|
|
15
|
+
if obscure_fields.include? token
|
16
|
+
"#{token[0,1]}***#{token[token.length-1,1]}"
|
17
|
+
else
|
18
|
+
token
|
19
|
+
end
|
20
|
+
end
|
21
|
+
Luffa.log_unix_cmd obscured.join(' ')
|
22
|
+
elsif merged_opts[:log_cmd]
|
23
|
+
Luffa.log_unix_cmd cmd
|
24
|
+
end
|
25
|
+
|
26
|
+
exit_on_err = merged_opts[:exit_on_nonzero_status]
|
27
|
+
unless exit_on_err
|
28
|
+
system 'set +e'
|
29
|
+
end
|
30
|
+
|
31
|
+
env_vars = merged_opts[:env_vars]
|
32
|
+
res = system(env_vars, cmd)
|
33
|
+
exit_code = $?.exitstatus
|
34
|
+
if res
|
35
|
+
Luffa.log_pass merged_opts[:pass_msg]
|
36
|
+
else
|
37
|
+
Luffa.log_fail merged_opts[:fail_msg]
|
38
|
+
exit exit_code if exit_on_err
|
39
|
+
end
|
40
|
+
system 'set -e'
|
41
|
+
exit_code
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
module Luffa
|
2
|
+
VERSION = '1.0.0'
|
3
|
+
|
4
|
+
# A model of a software release version that can be used to compare two versions.
|
5
|
+
#
|
6
|
+
# Calabash and RunLoop try very hard to comply with Semantic Versioning rules.
|
7
|
+
# However, the semantic versioning spec is incompatible with RubyGem's patterns
|
8
|
+
# for pre-release gems.
|
9
|
+
#
|
10
|
+
# > "But returning to the practical: No release version of SemVer is compatible with Rubygems." - _David Kellum_
|
11
|
+
#
|
12
|
+
# Calabash and RunLoop version numbers will be in the form `<major>.<minor>.<patch>[.pre<N>]`.
|
13
|
+
#
|
14
|
+
# @see http://semver.org/
|
15
|
+
# @see http://gravitext.com/2012/07/22/versioning.html
|
16
|
+
class Version
|
17
|
+
|
18
|
+
# @!attribute [rw] major
|
19
|
+
# @return [Integer] the major version
|
20
|
+
attr_accessor :major
|
21
|
+
|
22
|
+
# @!attribute [rw] minor
|
23
|
+
# @return [Integer] the minor version
|
24
|
+
attr_accessor :minor
|
25
|
+
|
26
|
+
# @!attribute [rw] patch
|
27
|
+
# @return [Integer] the patch version
|
28
|
+
attr_accessor :patch
|
29
|
+
|
30
|
+
# @!attribute [rw] pre
|
31
|
+
# @return [Boolean] true if this is a pre-release version
|
32
|
+
attr_accessor :pre
|
33
|
+
|
34
|
+
# @!attribute [rw] pre_version
|
35
|
+
# @return [Integer] if this is a pre-release version, returns the
|
36
|
+
# pre-release version; otherwise this is nil
|
37
|
+
attr_accessor :pre_version
|
38
|
+
|
39
|
+
# Creates a new Version instance with all the attributes set.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# version = Version.new(0.10.1)
|
43
|
+
# version.major => 0
|
44
|
+
# version.minor => 10
|
45
|
+
# version.patch => 1
|
46
|
+
# version.pre => false
|
47
|
+
# version.pre_release => nil
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# version = Version.new(1.6.3.pre5)
|
51
|
+
# version.major => 1
|
52
|
+
# version.minor => 6
|
53
|
+
# version.patch => 3
|
54
|
+
# version.pre => true
|
55
|
+
# version.pre_release => 5
|
56
|
+
#
|
57
|
+
# @param [String] version the version string to parse.
|
58
|
+
# @raise [ArgumentError] if version is not in the form 5, 6.1, 7.1.2, 8.2.3.pre1
|
59
|
+
def initialize(version)
|
60
|
+
tokens = version.strip.split('.')
|
61
|
+
count = tokens.count
|
62
|
+
if tokens.empty?
|
63
|
+
raise ArgumentError, "expected '#{version}' to be like 5, 6.1, 7.1.2, 8.2.3.pre1"
|
64
|
+
end
|
65
|
+
|
66
|
+
if count < 4 and tokens.any? { |elm| elm =~ /\D/ }
|
67
|
+
raise ArgumentError, "expected '#{version}' to be like 5, 6.1, 7.1.2, 8.2.3.pre1"
|
68
|
+
end
|
69
|
+
|
70
|
+
if count == 4
|
71
|
+
@pre = tokens[3]
|
72
|
+
pre_tokens = @pre.scan(/\D+|\d+/)
|
73
|
+
@pre_version = pre_tokens[1].to_i if pre_tokens.count == 2
|
74
|
+
end
|
75
|
+
|
76
|
+
@major, @minor, @patch = version.split('.').map(&:to_i)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns an string representation of this version.
|
80
|
+
# @return [String] a string in the form `<major>.<minor>.<patch>[.pre<N>]`
|
81
|
+
def to_s
|
82
|
+
str = [major, minor, patch].compact.join('.')
|
83
|
+
str = "#{str}.#{pre}" if pre
|
84
|
+
str
|
85
|
+
end
|
86
|
+
|
87
|
+
# Compare this version to another for equality.
|
88
|
+
# @param [Version] other the version to compare against
|
89
|
+
# @return [Boolean] true if this Version is the same as `other`
|
90
|
+
def == (other)
|
91
|
+
Version.compare(self, other) == 0
|
92
|
+
end
|
93
|
+
|
94
|
+
# Compare this version to another for inequality.
|
95
|
+
# @param [Version] other the version to compare against
|
96
|
+
# @return [Boolean] true if this Version is not the same as `other`
|
97
|
+
def != (other)
|
98
|
+
Version.compare(self, other) != 0
|
99
|
+
end
|
100
|
+
|
101
|
+
# Is this version less-than another version?
|
102
|
+
# @param [Version] other the version to compare against
|
103
|
+
# @return [Boolean] true if this Version is less-than `other`
|
104
|
+
def < (other)
|
105
|
+
Version.compare(self, other) < 0
|
106
|
+
end
|
107
|
+
|
108
|
+
# Is this version greater-than another version?
|
109
|
+
# @param [Version] other the version to compare against
|
110
|
+
# @return [Boolean] true if this Version is greater-than `other`
|
111
|
+
def > (other)
|
112
|
+
Version.compare(self, other) > 0
|
113
|
+
end
|
114
|
+
|
115
|
+
# Is this version less-than or equal to another version?
|
116
|
+
# @param [Version] other the version to compare against
|
117
|
+
# @return [Boolean] true if this Version is less-than or equal `other`
|
118
|
+
def <= (other)
|
119
|
+
Version.compare(self, other) <= 0
|
120
|
+
end
|
121
|
+
|
122
|
+
# Is this version greater-than or equal to another version?
|
123
|
+
# @param [Version] other the version to compare against
|
124
|
+
# @return [Boolean] true if this Version is greater-than or equal `other`
|
125
|
+
def >= (other)
|
126
|
+
Version.compare(self, other) >= 0
|
127
|
+
end
|
128
|
+
|
129
|
+
# Compare version `a` to version `b`.
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# compare Version.new(0.10.0), Version.new(0.9.0) => 1
|
133
|
+
# compare Version.new(0.9.0), Version.new(0.10.0) => -1
|
134
|
+
# compare Version.new(0.9.0), Version.new(0.9.0) => 0
|
135
|
+
#
|
136
|
+
# @return [Integer] an integer `(-1, 1)`
|
137
|
+
def self.compare(a, b)
|
138
|
+
|
139
|
+
if a.major != b.major
|
140
|
+
return a.major > b.major ? 1 : -1
|
141
|
+
end
|
142
|
+
|
143
|
+
if a.minor != b.minor
|
144
|
+
return a.minor.to_i > b.minor.to_i ? 1 : -1
|
145
|
+
end
|
146
|
+
|
147
|
+
if a.patch != b.patch
|
148
|
+
return a.patch.to_i > b.patch.to_i ? 1 : -1
|
149
|
+
end
|
150
|
+
|
151
|
+
return -1 if a.pre and (not a.pre_version) and b.pre_version
|
152
|
+
return 1 if a.pre_version and b.pre and (not b.pre_version)
|
153
|
+
|
154
|
+
return -1 if a.pre and (not b.pre)
|
155
|
+
return 1 if (not a.pre) and b.pre
|
156
|
+
|
157
|
+
return -1 if a.pre_version and (not b.pre_version)
|
158
|
+
return 1 if (not a.pre_version) and b.pre_version
|
159
|
+
|
160
|
+
if a.pre_version != b.pre_version
|
161
|
+
return a.pre_version.to_i > b.pre_version.to_i ? 1 : -1
|
162
|
+
end
|
163
|
+
0
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>allow-root</key>
|
6
|
+
<true/>
|
7
|
+
<key>authenticate-user</key>
|
8
|
+
<true/>
|
9
|
+
<key>class</key>
|
10
|
+
<string>user</string>
|
11
|
+
<key>comment</key>
|
12
|
+
<string>Rights for Instruments</string>
|
13
|
+
<key>created</key>
|
14
|
+
<real>409022991.255041</real>
|
15
|
+
<key>group</key>
|
16
|
+
<string>admin</string>
|
17
|
+
<key>identifier</key>
|
18
|
+
<string>com.apple.dt.instruments.dtsecurity.xpc</string>
|
19
|
+
<key>modified</key>
|
20
|
+
<real>409022991.255041</real>
|
21
|
+
<key>requirement</key>
|
22
|
+
<string>identifier "com.apple.dt.instruments.dtsecurity.xpc" and anchor apple</string>
|
23
|
+
<key>session-owner</key>
|
24
|
+
<false/>
|
25
|
+
<key>shared</key>
|
26
|
+
<true/>
|
27
|
+
<key>timeout</key>
|
28
|
+
<integer>36000</integer>
|
29
|
+
<key>tries</key>
|
30
|
+
<integer>10000</integer>
|
31
|
+
<key>version</key>
|
32
|
+
<integer>0</integer>
|
33
|
+
</dict>
|
34
|
+
</plist>
|