lib/calabash/android/device.rb in calabash-1.9.9.pre2 vs lib/calabash/android/device.rb in calabash-1.9.9.pre3

- old
+ new

@@ -1,10 +1,11 @@ require 'json' module Calabash module Android # A representation of a Calabash Android device. + # @!visibility private class Device < ::Calabash::Device attr_reader :adb def initialize(identifier, server) super @@ -13,10 +14,16 @@ http_client.on_error(Errno::ECONNREFUSED) do |server| port_forward(server.endpoint.port, server.test_server_port) end end + # @!visibility private + def change_server(new_server) + super(new_server) + port_forward(new_server.endpoint.port, new_server.test_server_port) + end + def self.default_serial serials = list_serials if Environment::DEVICE_IDENTIFIER index = serials.index(Environment::DEVICE_IDENTIFIER) @@ -154,11 +161,11 @@ if result['outcome'] != 'SUCCESS' raise "mapping \"#{query}\" with \"#{method_name}\" failed because: #{result['reason']}\n#{result['details']}" end - result['results'] + Calabash::QueryResult.create(result['results'], query) end def perform_action(action, *arguments) @logger.log "Action: #{action} - Arguments: #{arguments.join(', ')}" @@ -348,22 +355,21 @@ def calabash_server_finished_file_path(application) "/data/data/#{application.test_server.identifier}/files/calabash_finished.out" end - def calabash_server_failure_exists?(application) - cmd = "ls #{calabash_server_failure_file_path(application)}" + def adb_file_exists?(file) + cmd = "ls #{file}" + adb.shell(cmd, no_exit_code_check: true).chomp == file + end - adb.shell(cmd, no_exit_code_check: true).chomp == - calabash_server_failure_file_path(application) + def calabash_server_failure_exists?(application) + adb_file_exists?(calabash_server_failure_file_path(application)) end def calabash_server_finished_exists?(application) - cmd = "ls #{calabash_server_finished_file_path(application)}" - - adb.shell(cmd, no_exit_code_check: true).chomp == - calabash_server_finished_file_path(application) + adb_file_exists?(calabash_server_finished_file_path(application)) end def read_calabash_sever_failure(application) adb.shell("cat #{calabash_server_failure_file_path(application)}") end @@ -404,10 +410,24 @@ unless app_installed?(application.test_server.identifier) raise "The test-server '#{application.test_server.identifier}' is not installed" end + installed_app = installed_apps.find{|app| app[:package] == application.identifier} + installed_app_md5_checksum = md5_checksum(installed_app[:path]) + + if application.md5_checksum != installed_app_md5_checksum + raise "The specified app is not the same as the installed app (#{application.md5_checksum} != #{installed_app_md5_checksum})." + end + + installed_test_server = installed_apps.find{|app| app[:package] == application.test_server.identifier} + installed_test_server_md5_checksum = md5_checksum(installed_test_server[:path]) + + if application.test_server.md5_checksum != installed_test_server_md5_checksum + raise "The specified test-server is not the same as the installed test-server (#{application.test_server.md5_checksum} != #{installed_test_server_md5_checksum})." + end + ensure_screen_on # Clear any old error reports clear_calabash_server_report(application) @@ -534,10 +554,14 @@ if application.test_server.nil? raise ArgumentError, "No test server set for '#{application}'" end + unless app_installed?(application.identifier) + raise "The application #{application.identifier}' is not installed" + end + unless app_installed?(application.test_server.identifier) raise "The test-server '#{application.test_server.identifier}' is not installed" end cmd = "am instrument #{extras} #{application.test_server.identifier}/#{test_server_activity}" @@ -744,11 +768,11 @@ } gesture = Gestures::Gesture.double_tap(gesture_options) execute_gesture(Gestures::Gesture.with_parameters(gesture, - query_string: query.to_s, + query: query, timeout: options[:timeout])) end # @!visibility private def _long_press(query, options={}) @@ -783,22 +807,22 @@ duration = options[:duration] gesture = Gestures::Gesture.generate_swipe(from, to, time: duration) execute_gesture(Gestures::Gesture.with_parameters(gesture, - query_string: query.to_s, + query: query, timeout: options[:timeout])) end # @!visibility private def _pan_between(query_from, query_to, options={}) gesture = Gestures::Gesture.generate_swipe({x: 50, y: 50}, {x: 50, y: 50}, time: options[:duration]) gesture.gestures.first.touches[0].query = query_from gesture.gestures.first.touches[1].query = query_to execute_gesture(Gestures::Gesture.with_parameters(gesture, - query_string: query_to, + query: query_to, timeout: options[:timeout])) end # @!visibility private def _flick(query, from, to, options={}) @@ -811,20 +835,20 @@ duration = options[:duration] gesture = Gestures::Gesture.generate_swipe(from, to, time: duration, flick: true) execute_gesture(Gestures::Gesture.with_parameters(gesture, - query_string: query.to_s, + query: query, timeout: options[:timeout])) end # @!visibility private def _pinch(direction, query, options={}) gesture = Gestures::Gesture.pinch(direction) execute_gesture(Gestures::Gesture.with_parameters(gesture, - query_string: query.to_s, + query: query, timeout: options[:timeout])) end # @!visibility private def adb_uninstall_app(package) @@ -840,18 +864,59 @@ end end # @!visibility private def adb_install_app(application) + # Because of a bug in the latest version of ADB + # https://github.com/android/platform_system_core/blob/0f91887868e51de67bdf9aedc97fbcb044dc1969/adb/commandline.cpp#L1466 + # ADB now uses rm -f ... to remove the temporary application on the + # device, but not all devices (below a certain OS) supports this flag. + # The user will be unable to install the app and instead receive: + # RuntimeError: Could not install app 'com.xamarin.xtcandroidsample.test': rm failed for -f, No such file or directory + # We have rewritten the way adb handles app installation. It's a 3-step + # procedure: + # - Push the app binary to /data/local/tmp + # - Install the app binary using pm + # - Remove the temporary apk. @logger.log "Installing #{application.path}" - result = adb.command('install' , '-r', application.path, timeout: 60).lines.last - if result.downcase.chomp != 'success' - raise "Could not install app '#{application.identifier}': #{result.chomp}" + tmp_path = "/data/local/tmp/#{File.basename(application.path)}" + + begin + adb.command('push', application.path, tmp_path, timeout: 60) + rescue ADB::ADBCallError => e + raise "Failed to push the application to the device storage: '#{e.message}'" end - unless installed_packages.include?(application.identifier) - raise "App '#{application.identifier}' was not installed" + begin + result = nil + + begin + result = adb.shell("pm install -r #{tmp_path}", timeout: 60) + rescue ADB::ADBCallError => e + raise "Failed to install the application on device: '#{e.message}'" + end + + if result.lines.last.downcase.chomp != 'success' + raise "Could not install app '#{application.identifier}': #{result.chomp}" + end + + unless installed_packages.include?(application.identifier) + raise "App '#{application.identifier}' was not installed" + end + rescue => e + begin + adb.shell("rm #{tmp_path}") + rescue ADB::ADBCallError => _ + end + + raise e + end + + begin + adb.shell("rm #{tmp_path}") + rescue ADB::ADBCallError => e + raise "Failed to remove the tmp apk from device: #{e.message}" end end # @!visibility private def adb_clear_app_data(package)