run_loop 2.0.7 → 2.0.8
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 +4 -4
- data/lib/run_loop.rb +4 -0
- data/lib/run_loop/app.rb +90 -19
- data/lib/run_loop/detect_aut/detect.rb +96 -0
- data/lib/run_loop/detect_aut/errors.rb +127 -0
- data/lib/run_loop/detect_aut/xamarin_studio.rb +46 -0
- data/lib/run_loop/detect_aut/xcode.rb +136 -0
- data/lib/run_loop/environment.rb +43 -0
- data/lib/run_loop/ipa.rb +5 -0
- data/lib/run_loop/otool.rb +1 -1
- data/lib/run_loop/version.rb +1 -1
- metadata +7 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: a796cf1b151815127a2c4ee38c6fadf13be9cef8
         | 
| 4 | 
            +
              data.tar.gz: 40e6b8e223325fba4d2a13683afa1609b2f9348c
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d0b6d5bb57703550c6592a5d665c1d11d74db9075bf8b688156e2f6094efd6ab0e3efc72c19e9080228f1da8d003f8d115382f804303228a091d05043d3a7309
         | 
| 7 | 
            +
              data.tar.gz: 8b53742a9c3b4fe65036f387e93dd189ff350e57bd24ebd90bb7c24e1f6635543a675aafb9500bdf34b92640d61ed6adb5c8eb81e9c9833fcab2f6588d28d7a7
         | 
    
        data/lib/run_loop.rb
    CHANGED
    
    | @@ -17,6 +17,10 @@ require 'run_loop/plist_buddy' | |
| 17 17 | 
             
            require "run_loop/codesign"
         | 
| 18 18 | 
             
            require 'run_loop/app'
         | 
| 19 19 | 
             
            require 'run_loop/ipa'
         | 
| 20 | 
            +
            require "run_loop/detect_aut/errors"
         | 
| 21 | 
            +
            require "run_loop/detect_aut/xamarin_studio"
         | 
| 22 | 
            +
            require "run_loop/detect_aut/xcode"
         | 
| 23 | 
            +
            require "run_loop/detect_aut/detect"
         | 
| 20 24 | 
             
            require 'run_loop/sim_control'
         | 
| 21 25 | 
             
            require 'run_loop/device'
         | 
| 22 26 | 
             
            require 'run_loop/instruments'
         | 
    
        data/lib/run_loop/app.rb
    CHANGED
    
    | @@ -25,7 +25,8 @@ Bundle must: | |
| 25 25 |  | 
| 26 26 | 
             
            1. be a directory that exists,
         | 
| 27 27 | 
             
            2. have a .app extension,
         | 
| 28 | 
            -
            3.  | 
| 28 | 
            +
            3. contain an Info.plist,
         | 
| 29 | 
            +
            4. and the app binary (CFBundleExecutable) must exist
         | 
| 29 30 | 
             
            }
         | 
| 30 31 | 
             
                  end
         | 
| 31 32 | 
             
                end
         | 
| @@ -49,10 +50,12 @@ Bundle must: | |
| 49 50 | 
             
                def self.valid?(app_bundle_path)
         | 
| 50 51 | 
             
                  return false if app_bundle_path.nil?
         | 
| 51 52 |  | 
| 52 | 
            -
                  File. | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 53 | 
            +
                  return false if !File.directory?(app_bundle_path)
         | 
| 54 | 
            +
                  return false if !File.extname(app_bundle_path) == ".app"
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  return false if !self.info_plist_exist?(app_bundle_path)
         | 
| 57 | 
            +
                  return false if !self.executable_file_exist?(app_bundle_path)
         | 
| 58 | 
            +
                  true
         | 
| 56 59 | 
             
                end
         | 
| 57 60 |  | 
| 58 61 | 
             
                # Returns the Info.plist path.
         | 
| @@ -66,7 +69,7 @@ Bundle must: | |
| 66 69 | 
             
                # @raise [RuntimeError] If the plist cannot be read or the
         | 
| 67 70 | 
             
                #   CFBundleIdentifier is empty or does not exist.
         | 
| 68 71 | 
             
                def bundle_identifier
         | 
| 69 | 
            -
                  identifier = plist_buddy.plist_read( | 
| 72 | 
            +
                  identifier = plist_buddy.plist_read("CFBundleIdentifier", info_plist_path)
         | 
| 70 73 | 
             
                  unless identifier
         | 
| 71 74 | 
             
                    raise "Expected key 'CFBundleIdentifier' in '#{info_plist_path}'"
         | 
| 72 75 | 
             
                  end
         | 
| @@ -78,11 +81,28 @@ Bundle must: | |
| 78 81 | 
             
                # @raise [RuntimeError] If the plist cannot be read or the
         | 
| 79 82 | 
             
                #   CFBundleExecutable is empty or does not exist.
         | 
| 80 83 | 
             
                def executable_name
         | 
| 81 | 
            -
                   | 
| 82 | 
            -
                  unless  | 
| 84 | 
            +
                  name = plist_buddy.plist_read("CFBundleExecutable", info_plist_path)
         | 
| 85 | 
            +
                  unless name
         | 
| 83 86 | 
             
                    raise "Expected key 'CFBundleExecutable' in '#{info_plist_path}'"
         | 
| 84 87 | 
             
                  end
         | 
| 85 | 
            -
                   | 
| 88 | 
            +
                  name
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                # Returns the arches for the binary.
         | 
| 92 | 
            +
                def arches
         | 
| 93 | 
            +
                  @arches ||= lipo.info
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                # True if the app has been built for the simulator
         | 
| 97 | 
            +
                def simulator?
         | 
| 98 | 
            +
                  arches.include?("i386") || arches.include?("x86_64")
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                # True if the app has been built for physical devices
         | 
| 102 | 
            +
                def physical_device?
         | 
| 103 | 
            +
                  arches.any? do |arch|
         | 
| 104 | 
            +
                    arch[/arm/, 0]
         | 
| 105 | 
            +
                  end
         | 
| 86 106 | 
             
                end
         | 
| 87 107 |  | 
| 88 108 | 
             
                # Inspects the app's file for the server version
         | 
| @@ -115,7 +135,6 @@ Bundle must: | |
| 115 135 | 
             
                def executables
         | 
| 116 136 | 
             
                  executables = []
         | 
| 117 137 | 
             
                  Dir.glob("#{path}/**/*") do |file|
         | 
| 118 | 
            -
                    next if File.directory?(file)
         | 
| 119 138 | 
             
                    next if skip_executable_check?(file)
         | 
| 120 139 | 
             
                    if otool(file).executable?
         | 
| 121 140 | 
             
                      executables << file
         | 
| @@ -131,6 +150,30 @@ Bundle must: | |
| 131 150 |  | 
| 132 151 | 
             
                private
         | 
| 133 152 |  | 
| 153 | 
            +
                # @!visibility private
         | 
| 154 | 
            +
                def self.info_plist_exist?(app_bundle_path)
         | 
| 155 | 
            +
                  info_plist = File.join(app_bundle_path, "Info.plist")
         | 
| 156 | 
            +
                  File.exist?(info_plist)
         | 
| 157 | 
            +
                end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                # @!visibility private
         | 
| 160 | 
            +
                def self.executable_file_exist?(app_bundle_path)
         | 
| 161 | 
            +
                  return false if !self.info_plist_exist?(app_bundle_path)
         | 
| 162 | 
            +
                  info_plist = File.join(app_bundle_path, "Info.plist")
         | 
| 163 | 
            +
                  pbuddy = RunLoop::PlistBuddy.new
         | 
| 164 | 
            +
                  name = pbuddy.plist_read("CFBundleExecutable", info_plist)
         | 
| 165 | 
            +
                  if name
         | 
| 166 | 
            +
                    File.exist?(File.join(app_bundle_path, name))
         | 
| 167 | 
            +
                  else
         | 
| 168 | 
            +
                    false
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                # @!visibility private
         | 
| 173 | 
            +
                def lipo
         | 
| 174 | 
            +
                  @lipo ||= RunLoop::Lipo.new(path)
         | 
| 175 | 
            +
                end
         | 
| 176 | 
            +
             | 
| 134 177 | 
             
                # @!visibility private
         | 
| 135 178 | 
             
                def plist_buddy
         | 
| 136 179 | 
             
                  @plist_buddy ||= RunLoop::PlistBuddy.new
         | 
| @@ -150,17 +193,20 @@ Bundle must: | |
| 150 193 |  | 
| 151 194 | 
             
                # @!visibility private
         | 
| 152 195 | 
             
                def skip_executable_check?(file)
         | 
| 153 | 
            -
                   | 
| 196 | 
            +
                  File.directory?(file) ||
         | 
| 197 | 
            +
                    image?(file) ||
         | 
| 154 198 | 
             
                    text?(file) ||
         | 
| 155 199 | 
             
                    plist?(file) ||
         | 
| 156 200 | 
             
                    lproj_asset?(file) ||
         | 
| 157 201 | 
             
                    code_signing_asset?(file) ||
         | 
| 158 | 
            -
                    core_data_asset?(file)
         | 
| 202 | 
            +
                    core_data_asset?(file) ||
         | 
| 203 | 
            +
                    font?(file)
         | 
| 159 204 | 
             
                end
         | 
| 160 205 |  | 
| 161 206 | 
             
                # @!visibility private
         | 
| 162 207 | 
             
                def text?(file)
         | 
| 163 208 | 
             
                   extension = File.extname(file)
         | 
| 209 | 
            +
                   filename = File.basename(file)
         | 
| 164 210 |  | 
| 165 211 | 
             
                   extension == ".txt" ||
         | 
| 166 212 | 
             
                     extension == ".md" ||
         | 
| @@ -170,12 +216,25 @@ Bundle must: | |
| 170 216 | 
             
                     extension == ".yaml" ||
         | 
| 171 217 | 
             
                     extension == ".yml" ||
         | 
| 172 218 | 
             
                     extension == ".rtf" ||
         | 
| 173 | 
            -
             | 
| 219 | 
            +
             | 
| 220 | 
            +
                     ["NOTICE", "LICENSE", "README", "ABOUT"].any? do |elm|
         | 
| 221 | 
            +
                       filename[/#{elm}/]
         | 
| 222 | 
            +
                     end
         | 
| 174 223 | 
             
                end
         | 
| 175 224 |  | 
| 176 225 | 
             
                # @!visibility private
         | 
| 177 226 | 
             
                def image?(file)
         | 
| 178 | 
            -
                  file | 
| 227 | 
            +
                  extension = File.extname(file)
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                  extension == ".jpeg" ||
         | 
| 230 | 
            +
                  extension == ".jpg" ||
         | 
| 231 | 
            +
                  extension == ".gif" ||
         | 
| 232 | 
            +
                  extension == ".png" ||
         | 
| 233 | 
            +
                  extension == ".tiff" ||
         | 
| 234 | 
            +
                  extension == ".svg" ||
         | 
| 235 | 
            +
                  extension == ".pdf" ||
         | 
| 236 | 
            +
                  extension == ".car" ||
         | 
| 237 | 
            +
                  file[/iTunesArtwork/, 0]
         | 
| 179 238 | 
             
                end
         | 
| 180 239 |  | 
| 181 240 | 
             
                # @!visibility private
         | 
| @@ -186,9 +245,11 @@ Bundle must: | |
| 186 245 | 
             
                # @!visibility private
         | 
| 187 246 | 
             
                def lproj_asset?(file)
         | 
| 188 247 | 
             
                  extension = File.extname(file)
         | 
| 248 | 
            +
                  dir_extension = File.extname(File.dirname(file))
         | 
| 189 249 |  | 
| 190 | 
            -
                   | 
| 191 | 
            -
                     | 
| 250 | 
            +
                  dir_extension == ".lproj" ||
         | 
| 251 | 
            +
                    dir_extension == ".storyboard" ||
         | 
| 252 | 
            +
                    dir_extension == ".storyboardc" ||
         | 
| 192 253 | 
             
                    extension == ".strings" ||
         | 
| 193 254 | 
             
                    extension == ".xib" ||
         | 
| 194 255 | 
             
                    extension == ".nib"
         | 
| @@ -198,21 +259,31 @@ Bundle must: | |
| 198 259 | 
             
                def code_signing_asset?(file)
         | 
| 199 260 | 
             
                  name = File.basename(file)
         | 
| 200 261 | 
             
                  extension = File.extname(file)
         | 
| 262 | 
            +
                  dirname = File.basename(File.dirname(file))
         | 
| 201 263 |  | 
| 202 264 | 
             
                  name == "PkgInfo" ||
         | 
| 203 265 | 
             
                    name == "embedded" ||
         | 
| 204 266 | 
             
                    extension == ".mobileprovision" ||
         | 
| 205 267 | 
             
                    extension == ".xcent" ||
         | 
| 206 | 
            -
                     | 
| 268 | 
            +
                    dirname == "_CodeSignature"
         | 
| 207 269 | 
             
                end
         | 
| 208 270 |  | 
| 209 271 | 
             
                # @!visibility private
         | 
| 210 272 | 
             
                def core_data_asset?(file)
         | 
| 211 273 | 
             
                  extension = File.extname(file)
         | 
| 274 | 
            +
                  dir_extension = File.extname(File.dirname(file))
         | 
| 212 275 |  | 
| 213 | 
            -
                   | 
| 276 | 
            +
                  dir_extension == ".momd" ||
         | 
| 214 277 | 
             
                    extension == ".mom" ||
         | 
| 215 | 
            -
                    extension == ".db"
         | 
| 278 | 
            +
                    extension == ".db" ||
         | 
| 279 | 
            +
                    extension == ".omo"
         | 
| 280 | 
            +
                end
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                # @!visibility private
         | 
| 283 | 
            +
                def font?(file)
         | 
| 284 | 
            +
                  extension = File.extname(file)
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                  extension == ".tff" || extension == ".otf"
         | 
| 216 287 | 
             
                end
         | 
| 217 288 | 
             
              end
         | 
| 218 289 | 
             
            end
         | 
| @@ -0,0 +1,96 @@ | |
| 1 | 
            +
            module RunLoop
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              # @!visibility private
         | 
| 4 | 
            +
              module DetectAUT
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                # @!visibility private
         | 
| 7 | 
            +
                class Detect
         | 
| 8 | 
            +
                  include RunLoop::DetectAUT::Errors
         | 
| 9 | 
            +
                  include RunLoop::DetectAUT::XamarinStudio
         | 
| 10 | 
            +
                  include RunLoop::DetectAUT::Xcode
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # @!visibility private
         | 
| 13 | 
            +
                  def app_for_simulator
         | 
| 14 | 
            +
                    path = RunLoop::Environment.path_to_app_bundle
         | 
| 15 | 
            +
                    return RunLoop::App.new(path) if path
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    if xcode_project?
         | 
| 18 | 
            +
                      apps, search_dirs = detect_xcode_apps
         | 
| 19 | 
            +
                    elsif xamarin_project?
         | 
| 20 | 
            +
                      search_dirs = [solution_directory]
         | 
| 21 | 
            +
                      apps = candidate_apps(search_dirs.first)
         | 
| 22 | 
            +
                    else
         | 
| 23 | 
            +
                      apps = []
         | 
| 24 | 
            +
                      search_dirs = []
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    # If this is a Xamarin project, we've already searched the local
         | 
| 28 | 
            +
                    # directory tree for .app.
         | 
| 29 | 
            +
                    if apps.empty? && !xamarin_project?
         | 
| 30 | 
            +
                      search_dirs << File.expand_path("./")
         | 
| 31 | 
            +
                      apps = candidate_apps(File.expand_path("./"))
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    if apps.empty?
         | 
| 35 | 
            +
                      raise_no_simulator_app_found(search_dirs)
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    app = select_most_recent_app(apps)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    RunLoop.log_info2("Detected app at path:")
         | 
| 41 | 
            +
                    RunLoop.log_info2("#{app.path}")
         | 
| 42 | 
            +
                    time_str = mtime(app).strftime("%a %d %b %Y %H:%M:%S %Z")
         | 
| 43 | 
            +
                    RunLoop.log_info2("Modification time of app: #{time_str}")
         | 
| 44 | 
            +
                    RunLoop.log_info2("If this is incorrect, set the APP variable and/or rebuild your app")
         | 
| 45 | 
            +
                    RunLoop.log_info2("It is your responsibility to ensure you are testing the right app.")
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    app
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  # @!visibility private
         | 
| 51 | 
            +
                  # @param [Array<RunLoop::Detect>] apps
         | 
| 52 | 
            +
                  def select_most_recent_app(apps)
         | 
| 53 | 
            +
                    apps.max do |a, b|
         | 
| 54 | 
            +
                      mtime(a).to_i <=> mtime(b).to_i
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  # @!visibility private
         | 
| 59 | 
            +
                  # @param [String] bundle_path
         | 
| 60 | 
            +
                  def app_with_bundle(bundle_path)
         | 
| 61 | 
            +
                    RunLoop::App.new(bundle_path)
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  # @!visibility private
         | 
| 65 | 
            +
                  # @param [String] base_dir where to start the recursive search
         | 
| 66 | 
            +
                  def candidate_apps(base_dir)
         | 
| 67 | 
            +
                    candidates = []
         | 
| 68 | 
            +
                    Dir.glob("#{base_dir}/**/*.app").each do |bundle_path|
         | 
| 69 | 
            +
                      app = app_or_nil(bundle_path)
         | 
| 70 | 
            +
                      candidates << app if app
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                    candidates
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  # @!visibility private
         | 
| 76 | 
            +
                  # @param [String] bundle_path path to .app
         | 
| 77 | 
            +
                  def app_or_nil(bundle_path)
         | 
| 78 | 
            +
                    return nil if !RunLoop::App.valid?(bundle_path)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    app = app_with_bundle(bundle_path)
         | 
| 81 | 
            +
                    if app.simulator? && app.calabash_server_version
         | 
| 82 | 
            +
                      app
         | 
| 83 | 
            +
                    else
         | 
| 84 | 
            +
                      nil
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  # @!visibility private
         | 
| 89 | 
            +
                  # @param [RunLoop::Detect] app
         | 
| 90 | 
            +
                  def mtime(app)
         | 
| 91 | 
            +
                    path = File.join(app.path, app.executable_name)
         | 
| 92 | 
            +
                    File.mtime(path)
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
              end
         | 
| 96 | 
            +
            end
         | 
| @@ -0,0 +1,127 @@ | |
| 1 | 
            +
            module RunLoop
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              # @!visibility private
         | 
| 4 | 
            +
              class SolutionMissingError < RuntimeError ; end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              # @!visibility private
         | 
| 7 | 
            +
              class XcodeprojMissingError < RuntimeError; end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              # @!visibility private
         | 
| 10 | 
            +
              class MultipleXcodeprojError < RuntimeError; end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              # @!visibility private
         | 
| 13 | 
            +
              class NoSimulatorAppFoundError < RuntimeError; end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              # @!visibility private
         | 
| 16 | 
            +
              module DetectAUT
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                # @!visibility private
         | 
| 19 | 
            +
                module Errors
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  # @!visibility private
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # Raised when XCODEPROJ is defined, but does not exist
         | 
| 24 | 
            +
                  def raise_xcodeproj_missing(xcodeproj)
         | 
| 25 | 
            +
                    raise RunLoop::XcodeprojMissingError,
         | 
| 26 | 
            +
            %Q[The XCODEPROJ environment variable has been set to:
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            #{xcodeproj}
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            but no directory exists at that path.
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            You should only set XCODEPROJ variable if your Calabash project has more than
         | 
| 33 | 
            +
            one .xcodeproj directory or your .xcodeproj directory is located above the
         | 
| 34 | 
            +
            current directory (where you run `cucumber` from).  Calabash will discover the
         | 
| 35 | 
            +
            .xcodeproj if is below the current working directory.
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            # .xcodeproj is above the working directory
         | 
| 38 | 
            +
            $ ls ../*.xcodeproj
         | 
| 39 | 
            +
            MyApp.xcodeproj
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            $ XCODEPROJ=../*.xcodeproj cucumber
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            # There is more than one .xcodeproj
         | 
| 44 | 
            +
            $ ls *.xcodeproj
         | 
| 45 | 
            +
            MyiOSApp.xcodeproj
         | 
| 46 | 
            +
            MyMacApp.xcodeproj
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            $ XCODEPROJ=MyiOSApp.xcodeproj
         | 
| 49 | 
            +
            ]
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  # @!visibility private
         | 
| 53 | 
            +
                  #
         | 
| 54 | 
            +
                  # Raised when there are more than > 1 .xcodeproj in **/*
         | 
| 55 | 
            +
                  # Is not raised when there are 0 .xcodeproj in **/*
         | 
| 56 | 
            +
                  #
         | 
| 57 | 
            +
                  # @param [Array<String>] projects a list of paths to .xcodeproj
         | 
| 58 | 
            +
                  def raise_multiple_xcodeproj(projects)
         | 
| 59 | 
            +
                  raise RunLoop::MultipleXcodeprojError,
         | 
| 60 | 
            +
            %Q[Found multiple .xcodeproj directories:
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            #{projects.each { |path| puts "    #{path}" }}
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            Which project contains the target of the application you are trying to test?
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            Set the XCODEPROJ variable to specify the correct .xcodeproj directory.
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            # Examples
         | 
| 69 | 
            +
            $ XCODEPROJ="../MyApp.xcodeproj" cucumber
         | 
| 70 | 
            +
            $ XCODEPROJ="iOS/MyApp.xcodeproj" cucumber
         | 
| 71 | 
            +
            ]
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  # @!visibility private
         | 
| 75 | 
            +
                  #
         | 
| 76 | 
            +
                  # Raised when SOLUTION is defined but does not exist
         | 
| 77 | 
            +
                  def raise_solution_missing(solution)
         | 
| 78 | 
            +
                    raise RunLoop::SolutionMissingError,
         | 
| 79 | 
            +
            %Q[The SOLUTION environment variable has been set to:
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            #{solution}
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            but no solution exists at that path.
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            You should only set SOLUTION variable if your .sln is not located in
         | 
| 86 | 
            +
            the current directory or in the directory just above the current
         | 
| 87 | 
            +
            directory (where you run `cucumber` from). Calabash will discover the
         | 
| 88 | 
            +
            .snl if it is in the current working directory or the directory just
         | 
| 89 | 
            +
            above.
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            # Calabash will discover solution - don't set SOLUTION
         | 
| 92 | 
            +
            $ ls *.sln => MyApp.sln
         | 
| 93 | 
            +
            $ ls ../*.sln => MyApp.sln
         | 
| 94 | 
            +
             | 
| 95 | 
            +
            # Calabash will _not_ discover solution
         | 
| 96 | 
            +
            $ ls ../../*.sln => MyApp.sln
         | 
| 97 | 
            +
            $ SOLUTION=../../MyApp.sln cucumber
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            $ ls project/*.sln => MyApp.sln
         | 
| 100 | 
            +
            $ SOLUTION=project/MyApp.sln cucumber
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            $ ls ~/some/other/directory/*.sln => MyApp.sln
         | 
| 103 | 
            +
            $ SOLUTION=~/some/other/directory/MyApp.sln
         | 
| 104 | 
            +
            ]
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  # @!visibility private
         | 
| 108 | 
            +
                  # Raised when no app can found by the discovery algorithm
         | 
| 109 | 
            +
                  def raise_no_simulator_app_found(search_directories)
         | 
| 110 | 
            +
                    raise RunLoop::NoSimulatorAppFoundError,
         | 
| 111 | 
            +
                          %Q[Searched these directories:
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            #{search_directories.each { |path| puts "    #{path}" }}
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            but could not find any .app for the simulator that contains the Calabash iOS
         | 
| 116 | 
            +
            server.
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            Make sure you have built your app for a simulator target from Xcode.
         | 
| 119 | 
            +
             | 
| 120 | 
            +
            If you testing a stand-alone .app (you don't have an Xcode project), put your
         | 
| 121 | 
            +
            .app in the same directory (or below) the directory you run `cucumber` from.
         | 
| 122 | 
            +
            ]
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
              end
         | 
| 126 | 
            +
            end
         | 
| 127 | 
            +
             | 
| @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            module RunLoop
         | 
| 2 | 
            +
              # @!visibility private
         | 
| 3 | 
            +
              module DetectAUT
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                # @!visibility private
         | 
| 6 | 
            +
                module XamarinStudio
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  # @!visibility private
         | 
| 9 | 
            +
                  def xamarin_project?
         | 
| 10 | 
            +
                    solution_directory != nil
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  # @!visibility private
         | 
| 14 | 
            +
                  def solution_directory
         | 
| 15 | 
            +
                    solution = RunLoop::Environment.solution
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    if solution && !File.exist?(solution)
         | 
| 18 | 
            +
                      raise_solution_missing(solution)
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    # SOLUTION defined and exists
         | 
| 22 | 
            +
                    return File.dirname(solution) if solution
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    solution_dir = find_solution_directory
         | 
| 25 | 
            +
                    return nil if solution_dir.nil?
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    solution_dir
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # @!visibility private
         | 
| 31 | 
            +
                  def find_solution_directory
         | 
| 32 | 
            +
                    pwd = Dir.pwd
         | 
| 33 | 
            +
                    solutions = Dir.glob("#{pwd}/*.sln")
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    if solutions.empty?
         | 
| 36 | 
            +
                      solutions = Dir.glob("#{pwd}/../*.sln")
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    return nil if solutions.empty?
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    File.expand_path(File.dirname(solutions.first))
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         | 
| 46 | 
            +
             | 
| @@ -0,0 +1,136 @@ | |
| 1 | 
            +
            module RunLoop
         | 
| 2 | 
            +
              # @!visibility private
         | 
| 3 | 
            +
              module DetectAUT
         | 
| 4 | 
            +
                # @!visibility private
         | 
| 5 | 
            +
                module Xcode
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  # @!visibility private
         | 
| 8 | 
            +
                  def xcode_project?
         | 
| 9 | 
            +
                    xcodeproj != nil
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # @!visibility private
         | 
| 13 | 
            +
                  def xcodeproj
         | 
| 14 | 
            +
                    xcodeproj = RunLoop::Environment.xcodeproj
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    if xcodeproj && !File.directory?(xcodeproj)
         | 
| 17 | 
            +
                      raise_xcodeproj_missing(xcodeproj)
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    # XCODEPROJ defined and exists
         | 
| 21 | 
            +
                    return xcodeproj if xcodeproj
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    projects = find_xcodeproj
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    return nil if projects.empty?
         | 
| 26 | 
            +
                    return projects[0] if projects.count == 1
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    raise_multiple_xcodeproj(projects)
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  # @!visibility private
         | 
| 32 | 
            +
                  def find_xcodeproj
         | 
| 33 | 
            +
                    Dir.glob("#{Dir.pwd}/**/*.xcodeproj")
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  # @!visibility private
         | 
| 37 | 
            +
                  def detect_xcode_apps
         | 
| 38 | 
            +
                    dirs_to_search = derived_data_search_dirs
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    dir_from_prefs = xcode_preferences_search_dir
         | 
| 41 | 
            +
                    if dir_from_prefs
         | 
| 42 | 
            +
                      dirs_to_search << dir_from_prefs
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    apps = []
         | 
| 46 | 
            +
                    dirs_to_search.each do |dir|
         | 
| 47 | 
            +
                      # defined in detect_aut/apps.rb
         | 
| 48 | 
            +
                      candidates = candidate_apps(dir)
         | 
| 49 | 
            +
                      apps = apps.concat(candidates)
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    return apps, dirs_to_search
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  # @!visibility private
         | 
| 56 | 
            +
                  PLIST_KEYS = {
         | 
| 57 | 
            +
                    :workspace => "WorkspacePath",
         | 
| 58 | 
            +
                    :shared_build => "IDESharedBuildFolderName",
         | 
| 59 | 
            +
                    :custom_build => "IDECustomBuildProductsPath"
         | 
| 60 | 
            +
                  }
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  # @!visibility private
         | 
| 63 | 
            +
                  # TODO Needs unit tests
         | 
| 64 | 
            +
                  def derived_data_search_dirs
         | 
| 65 | 
            +
                    project = xcodeproj
         | 
| 66 | 
            +
                    project_name = File.basename(project)
         | 
| 67 | 
            +
                    matches = []
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    # WorkspacePath could be a .xcodeproj or .xcworkspace
         | 
| 70 | 
            +
                    #
         | 
| 71 | 
            +
                    # # Exact match.
         | 
| 72 | 
            +
                    #     xcodeproj = path/to/MyApp/MyApp.xcodeproj
         | 
| 73 | 
            +
                    # WorkspacePath = path/to/MyApp/MyApp.xcodeproj
         | 
| 74 | 
            +
                    #
         | 
| 75 | 
            +
                    # # CocoaPods projects are often configured like this.  As are legacy
         | 
| 76 | 
            +
                    # # projects that have been added to a new workspace.
         | 
| 77 | 
            +
                    #     xcodeproj = path/to/MyApp/MyApp.xcodeproj
         | 
| 78 | 
            +
                    # WorkspacePath = path/to/MyApp/MyApp.xcworkspace
         | 
| 79 | 
            +
                    #
         | 
| 80 | 
            +
                    # # This is the Xcode default when creating new iOS project
         | 
| 81 | 
            +
                    #     xcodeproj = path/to/MyApp/MyApp/MyApp.xcodeproj
         | 
| 82 | 
            +
                    # WorkspacePath = path/to/MyApp/MyApp.xcworkspace
         | 
| 83 | 
            +
                    key = PLIST_KEYS[:workspace]
         | 
| 84 | 
            +
                    Dir.glob("#{derived_data}/*/info.plist") do |plist|
         | 
| 85 | 
            +
                      workspace = pbuddy.plist_read(key, plist)
         | 
| 86 | 
            +
                      if workspace == project
         | 
| 87 | 
            +
                        matches << File.dirname(plist)
         | 
| 88 | 
            +
                      else
         | 
| 89 | 
            +
                        base_dir = File.dirname(workspace)
         | 
| 90 | 
            +
                        if File.exist?(File.join(base_dir, project_name))
         | 
| 91 | 
            +
                          matches << File.dirname(plist)
         | 
| 92 | 
            +
                        elsif !Dir.glob("#{File.dirname(workspace)}/*/#{project_name}").empty?
         | 
| 93 | 
            +
                          matches << File.dirname(plist)
         | 
| 94 | 
            +
                        elsif !Dir.glob("#{File.dirname(workspace)}/**/#{project_name}").empty?
         | 
| 95 | 
            +
                          matches << File.dirname(plist)
         | 
| 96 | 
            +
                        end
         | 
| 97 | 
            +
                      end
         | 
| 98 | 
            +
                    end
         | 
| 99 | 
            +
                    matches
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  # @!visibility private
         | 
| 103 | 
            +
                  def xcode_preferences_search_dir
         | 
| 104 | 
            +
                    plist = xcode_preferences_plist
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    shared_build_folder = pbuddy.plist_read(PLIST_KEYS[:shared_build], plist)
         | 
| 107 | 
            +
                    custom_build_folder = pbuddy.plist_read(PLIST_KEYS[:custom_build], plist)
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    if shared_build_folder
         | 
| 110 | 
            +
                      File.join(derived_data, shared_build_folder, "Products")
         | 
| 111 | 
            +
                    elsif custom_build_folder
         | 
| 112 | 
            +
                      File.join(File.dirname(xcodeproj), custom_build_folder)
         | 
| 113 | 
            +
                    else
         | 
| 114 | 
            +
                      nil
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  # @!visibility private
         | 
| 119 | 
            +
                  def derived_data
         | 
| 120 | 
            +
                    RunLoop::Environment.derived_data ||
         | 
| 121 | 
            +
                      File.expand_path("~/Library/Developer/Xcode/DerivedData")
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  # @!visibility private
         | 
| 125 | 
            +
                  def xcode_preferences_plist
         | 
| 126 | 
            +
                    File.expand_path('~/Library/Preferences/com.apple.dt.Xcode.plist')
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  # @!visibility private
         | 
| 130 | 
            +
                  def pbuddy
         | 
| 131 | 
            +
                    @pbuddy ||= RunLoop::PlistBuddy.new
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
            end
         | 
| 136 | 
            +
             | 
    
        data/lib/run_loop/environment.rb
    CHANGED
    
    | @@ -38,6 +38,49 @@ module RunLoop | |
| 38 38 | 
             
                  ENV["DEVICE_ENDPOINT"]
         | 
| 39 39 | 
             
                end
         | 
| 40 40 |  | 
| 41 | 
            +
                # Returns the value of XCODEPROJ which can be used to specify an Xcode
         | 
| 42 | 
            +
                # project directory (my.xcodeproj).
         | 
| 43 | 
            +
                #
         | 
| 44 | 
            +
                # This is useful if your project has multiple xcodeproj directories.
         | 
| 45 | 
            +
                #
         | 
| 46 | 
            +
                # Most users should not set this variable.
         | 
| 47 | 
            +
                def self.xcodeproj
         | 
| 48 | 
            +
                  value = ENV["XCODEPROJ"]
         | 
| 49 | 
            +
                  if value.nil? || value == ""
         | 
| 50 | 
            +
                    return nil
         | 
| 51 | 
            +
                  else
         | 
| 52 | 
            +
                    File.expand_path(value)
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                # Returns the value of DERIVED_DATA which can be used to specify an
         | 
| 57 | 
            +
                # alternative DerivedData directory.
         | 
| 58 | 
            +
                #
         | 
| 59 | 
            +
                # The default is ~/Library/Xcode/DerivedData, but Xcode allows you to
         | 
| 60 | 
            +
                # change this value.
         | 
| 61 | 
            +
                def self.derived_data
         | 
| 62 | 
            +
                  value = ENV["DERIVED_DATA"]
         | 
| 63 | 
            +
                  if value.nil? || value == ""
         | 
| 64 | 
            +
                    nil
         | 
| 65 | 
            +
                  else
         | 
| 66 | 
            +
                    File.expand_path(value)
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                # Returns the value of SOLUTION which can be used to specify a
         | 
| 71 | 
            +
                # Xamarin Studio .sln
         | 
| 72 | 
            +
                #
         | 
| 73 | 
            +
                # This is useful if your project has multiple solutions (.sln)
         | 
| 74 | 
            +
                # and Calabash cannot detect the correct one.
         | 
| 75 | 
            +
                def self.solution
         | 
| 76 | 
            +
                  value = ENV["SOLUTION"]
         | 
| 77 | 
            +
                  if value.nil? || value == ""
         | 
| 78 | 
            +
                    nil
         | 
| 79 | 
            +
                  else
         | 
| 80 | 
            +
                    File.expand_path(value)
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 41 84 | 
             
                # Returns the value of TRACE_TEMPLATE; the Instruments template to use
         | 
| 42 85 | 
             
                # during testing.
         | 
| 43 86 | 
             
                def self.trace_template
         | 
    
        data/lib/run_loop/ipa.rb
    CHANGED
    
    | @@ -45,6 +45,11 @@ module RunLoop | |
| 45 45 | 
             
                  app.executable_name
         | 
| 46 46 | 
             
                end
         | 
| 47 47 |  | 
| 48 | 
            +
                # Returns the arches for the binary.
         | 
| 49 | 
            +
                def arches
         | 
| 50 | 
            +
                  app.arches
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 48 53 | 
             
                # Inspects the app's executables for the server version
         | 
| 49 54 | 
             
                # @return[RunLoop::Version] a version instance
         | 
| 50 55 | 
             
                def calabash_server_version
         | 
    
        data/lib/run_loop/otool.rb
    CHANGED
    
    
    
        data/lib/run_loop/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: run_loop
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2.0. | 
| 4 | 
            +
              version: 2.0.8
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Karl Krukow
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2016- | 
| 11 | 
            +
            date: 2016-03-09 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: json
         | 
| @@ -296,6 +296,10 @@ files: | |
| 296 296 | 
             
            - lib/run_loop/codesign.rb
         | 
| 297 297 | 
             
            - lib/run_loop/core.rb
         | 
| 298 298 | 
             
            - lib/run_loop/core_simulator.rb
         | 
| 299 | 
            +
            - lib/run_loop/detect_aut/detect.rb
         | 
| 300 | 
            +
            - lib/run_loop/detect_aut/errors.rb
         | 
| 301 | 
            +
            - lib/run_loop/detect_aut/xamarin_studio.rb
         | 
| 302 | 
            +
            - lib/run_loop/detect_aut/xcode.rb
         | 
| 299 303 | 
             
            - lib/run_loop/device.rb
         | 
| 300 304 | 
             
            - lib/run_loop/directory.rb
         | 
| 301 305 | 
             
            - lib/run_loop/dot_dir.rb
         | 
| @@ -369,3 +373,4 @@ specification_version: 4 | |
| 369 373 | 
             
            summary: The bridge between Calabash iOS and Xcode command-line tools like instruments
         | 
| 370 374 | 
             
              and simctl.
         | 
| 371 375 | 
             
            test_files: []
         | 
| 376 | 
            +
            has_rdoc: 
         |