Top Level Namespace

Includes:
REXML

Defined Under Namespace

Modules: Calabash

Constant Summary collapse

WAIT_TIMEOUT =
ENV['WAIT_TIMEOUT'] || 30
STEP_PAUSE =
(ENV['STEP_PAUSE'] || 0.5).to_f

Instance Method Summary collapse

Instance Method Details

#build_test_server_if_needed(app_path) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
# File 'lib/calabash-android/helpers.rb', line 42

def build_test_server_if_needed(app_path)
  unless File.exist?(test_server_path(app_path))
    if ARGV.include? "--no-build"
      puts "No test server found for this combination of app and calabash version. Exiting!"
      exit 1
    else
      puts "No test server found for this combination of app and calabash version. Recreating test server."
      calabash_build(app_path)
    end
  end
end

#checksum(file_path) ⇒ Object



32
33
34
35
# File 'lib/calabash-android/helpers.rb', line 32

def checksum(file_path)
  require 'digest/md5'
  Digest::MD5.file(file_path).hexdigest
end

#default_keystoreObject



97
98
99
100
101
102
103
# File 'lib/calabash-android/helpers.rb', line 97

def default_keystore
  {
    "keystore_location" => File.expand_path(File.join(ENV["HOME"], "/.android/debug.keystore")),
    "keystore_password" => "android",
    "keystore_alias" => "androiddebugkey",
  }
end

#extract_md5_fingerprint(fingerprints) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/calabash-android/helpers.rb', line 158

def extract_md5_fingerprint(fingerprints)
  log fingerprints

  m = fingerprints.scan(/MD5.*((?:[a-fA-F\d]{2}:){15}[a-fA-F\d]{2})/).flatten
  raise "No MD5 fingerprint found:\n #{fingerprints}" if m.empty?
  m.first
end

#fail_if_key_missing(map, key) ⇒ Object



105
106
107
# File 'lib/calabash-android/helpers.rb', line 105

def fail_if_key_missing(map, key)
  raise "Found .calabash_settings but no #{key} defined." unless map[key]
end

#fingerprint_from_apk(app_path) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/calabash-android/helpers.rb', line 136

def fingerprint_from_apk(app_path)
  Dir.mktmpdir do |tmp_dir|
    Dir.chdir(tmp_dir) do
      FileUtils.cp(app_path, "app.apk")
      FileUtils.mkdir("META-INF")
      Zip::ZipFile.foreach("app.apk") do |z|
        z.extract if /^META-INF\/\w+.(RSA|rsa)/ =~ z.name
      end
      rsa_files = Dir["#{tmp_dir}/META-INF/*"]
      raise "No RSA file found in META-INF. Cannot proceed." if rsa_files.empty?
      raise "More than one RSA file found in META-INF. Cannot proceed." if rsa_files.length > 1

      cmd = "#{keytool_path} -v -printcert -file #{rsa_files.first}"
      log cmd
      fingerprints = `#{cmd}`
      md5_fingerprint = extract_md5_fingerprint(fingerprints)
      log "MD5 fingerprint for signing cert (#{app_path}): #{md5_fingerprint}"
      md5_fingerprint
    end
  end
end

#fingerprint_from_keystoreObject



125
126
127
128
129
130
131
132
133
134
# File 'lib/calabash-android/helpers.rb', line 125

def fingerprint_from_keystore
  keystore_info = read_keystore_info
  cmd = "#{keytool_path} -v -list -alias #{keystore_info["keystore_alias"]} -keystore #{keystore_info["keystore_location"]} -storepass #{keystore_info["keystore_password"]}"

  log cmd
  fingerprints = `#{cmd}`
  md5_fingerprint = extract_md5_fingerprint(fingerprints)
  log "MD5 fingerprint for keystore (#{keystore_info["keystore_location"]}): #{md5_fingerprint}"
  md5_fingerprint
end

#is_windows?Boolean

Returns:

  • (Boolean)


166
167
168
169
# File 'lib/calabash-android/helpers.rb', line 166

def is_windows?
  require 'rbconfig'
  (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
end

#keytool_pathObject



117
118
119
120
121
122
123
# File 'lib/calabash-android/helpers.rb', line 117

def keytool_path
  if is_windows?
    "\"#{ENV["JAVA_HOME"]}/bin/keytool.exe\""
  else
    "keytool"
  end
end

#log(message, error = false) ⇒ Object



171
172
173
# File 'lib/calabash-android/helpers.rb', line 171

def log(message, error = false)
  $stdout.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{message}" if (error or ARGV.include? "-v" or ARGV.include? "--verbose")
end

#main_activity(app) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/calabash-android/helpers.rb', line 16

def main_activity(app)
  manifest = Document.new(manifest(app))
  main_activity = manifest.elements["//action[@name='android.intent.action.MAIN']/../.."].attributes['name']
  #Handle situation where main activity is on the form '.class_name'
  if main_activity.start_with? "."
    main_activity = package_name(app) + main_activity
  elsif not main_activity.include? "." #This is undocumentet behaviour but Android seems to accept shorthand naming that does not start with '.'
    main_activity = "#{package_name(app)}.#{main_activity}"
  end
  main_activity
end

#manifest(app) ⇒ Object



28
29
30
# File 'lib/calabash-android/helpers.rb', line 28

def manifest(app)
  `java -jar "#{File.dirname(__FILE__)}/lib/manifest_extractor.jar" "#{app}"`
end

#package_name(app) ⇒ Object



8
9
10
11
12
13
14
# File 'lib/calabash-android/helpers.rb', line 8

def package_name(app)
  require 'rexml/document'
  require 'rexml/xpath'

  manifest = Document.new(manifest(app))
  manifest.root.attributes['package']
end

#put_in_quotes(s) ⇒ Object



113
114
115
# File 'lib/calabash-android/helpers.rb', line 113

def put_in_quotes(s)
  %Q{"#{s}"}
end

#read_keystore_infoObject



83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/calabash-android/helpers.rb', line 83

def read_keystore_info
  keystore = default_keystore

  if File.exist? ".calabash_settings"
    keystore = JSON.parse(IO.read(".calabash_settings"))
    fail_if_key_missing(keystore, "keystore_location")
    fail_if_key_missing(keystore, "keystore_password")
    fail_if_key_missing(keystore, "keystore_alias")
    keystore["keystore_location"] = File.expand_path(keystore["keystore_location"])
  end
  keystore["keystore_location"] = put_in_quotes(remove_quotes(keystore["keystore_location"]))
  keystore
end

#remove_quotes(s) ⇒ Object



109
110
111
# File 'lib/calabash-android/helpers.rb', line 109

def remove_quotes(s)
  s.gsub(/"/, "")
end

#resign_apk(app_path) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
# File 'lib/calabash-android/helpers.rb', line 54

def resign_apk(app_path)
  Dir.mktmpdir do |tmp_dir|
    log "Resign apk"
    unsigned_path = File.join(tmp_dir, 'unsigned.apk')
    FileUtils.cp(app_path, unsigned_path)

    `java -jar "#{File.dirname(__FILE__)}/lib/unsign.jar" "#{unsigned_path}"`

    sign_apk(unsigned_path, app_path)
  end
end

#sign_apk(app_path, dest_path) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/calabash-android/helpers.rb', line 66

def sign_apk(app_path, dest_path)
  keystore = read_keystore_info()

  if is_windows?
    jarsigner_path = "\"#{ENV["JAVA_HOME"]}/bin/jarsigner.exe\""
  else
    jarsigner_path = "jarsigner"
  end

  cmd = "#{jarsigner_path} -sigalg MD5withRSA -digestalg SHA1 -signedjar #{dest_path} -storepass #{keystore["keystore_password"]} -keystore #{keystore["keystore_location"]} #{app_path} #{keystore["keystore_alias"]}"
  log cmd
  unless system(cmd)
    puts "jarsigner command: #{cmd}"
    raise "Could not sign app (#{app_path}"
  end
end

#test_server_path(apk_file_path) ⇒ Object



37
38
39
# File 'lib/calabash-android/helpers.rb', line 37

def test_server_path(apk_file_path)
  "test_servers/#{checksum(apk_file_path)}_#{Calabash::Android::VERSION}.apk"
end