Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add failed driver guard to FileManager Appium API #709

Merged
merged 8 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# TBD

## Enhancements

- Failed driver guards added to File Manager API [709](https://github.com/bugsnag/maze-runner/pull/709)

# 9.22.0 - 2025/01/08

## Enhancements
Expand Down
1 change: 1 addition & 0 deletions bin/maze-runner
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require_relative '../lib/utils/deep_merge'
require_relative '../lib/maze'

require_relative '../lib/maze/appium_server'
require_relative '../lib/maze/api/appium/manager'
require_relative '../lib/maze/api/appium/file_manager'
require_relative '../lib/maze/api/cucumber/scenario'
require_relative '../lib/maze/api/exit_code'
Expand Down
40 changes: 32 additions & 8 deletions lib/maze/api/appium/file_manager.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
require_relative '../../helper'
require_relative './manager'

module Maze
module Api
module Appium
# Provides operations for working with files during Appium runs.
class FileManager
# param driver
def initialize
@driver = Maze.driver
end

class FileManager < Maze::Api::Appium::Manager
# Creates a file with the given contents on the device (using Appium). The file will be located in the app's
# documents directory for iOS. On Android, it will be /sdcard/Android/data/<app-id>/files unless
# Maze.config.android_app_files_directory has been set.
# @param contents [String] Content of the file to be written
# @param filename [String] Name (with no path) of the file to be written on the device
def write_app_file(contents, filename)
if failed_driver?
$logger.error 'Cannot write file to device - Appium driver failed.'
return
end

path = case Maze::Helper.get_current_platform
when 'ios'
"@#{@driver.app_id}/Documents/#{filename}"
Expand All @@ -24,6 +27,14 @@ def write_app_file(contents, filename)

$logger.trace "Pushing file to '#{path}' with contents: #{contents}"
@driver.push_file(path, contents)
true
rescue Selenium::WebDriver::Error::UnknownError => e
$logger.error "Error writing file to device: #{e.message}"
false
rescue Selenium::WebDriver::Error::ServerError => e
# Assume the remote appium session has stopped, so crash out of the session
fail_driver
raise e
end

# Attempts to retrieve a given file from the device (using Appium). The default location for the file will be
Expand All @@ -32,8 +43,13 @@ def write_app_file(contents, filename)
# @param filename [String] Name (with no path) of the file to be retrieved from the device
# @param directory [String] Directory on the device where the file is located (optional)
def read_app_file(filename, directory = nil)
if failed_driver?
$logger.error 'Cannot read file from device - Appium driver failed.'
return
end

if directory
path = directory
path = "#{directory}/#{filename}"
else
path = case Maze::Helper.get_current_platform
when 'ios'
Expand All @@ -45,7 +61,15 @@ def read_app_file(filename, directory = nil)
end

$logger.trace "Attempting to read file from '#{path}'"
file = @driver.pull_file(path)
@driver.pull_file(path)
true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is attempting to retrieve a file this should be the file pulled rather than the success metric?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gosh, you're absolutely right - I'd assumed the file is written straight to file, but it is returned in memory by the method call. Good spot.

rescue Selenium::WebDriver::Error::UnknownError => e
$logger.error "Error reading file from device: #{e.message}"
false
rescue Selenium::WebDriver::Error::ServerError => e
# Assume the remote appium session has stopped, so crash out of the session
fail_driver
raise e
end
end
end
Expand Down
20 changes: 20 additions & 0 deletions lib/maze/api/appium/manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Maze
module Api
module Appium
# Base class for all Appium managers.
class Manager
def initialize
@driver = Maze.driver
end

def failed_driver?
@driver.failed?
end

def fail_driver
@driver.fail_driver
end
end
end
end
end
78 changes: 78 additions & 0 deletions test/api/appium/file_manager_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
require 'selenium-webdriver'

require_relative '../../test_helper'
require_relative '../../../lib/maze'
require_relative '../../../lib/maze/api/appium/file_manager'

module Maze
module Api
module Appium
class FileManagerTest < Test::Unit::TestCase

def setup()
$logger = mock('logger')
@mock_driver = mock('driver')
Maze.driver = @mock_driver
@manager = Maze::Api::Appium::FileManager.new
end

def test_write_app_file_failed_driver
@mock_driver.expects(:failed?).returns(true)
$logger.expects(:error).with("Cannot write file to device - Appium driver failed.")

@manager.write_app_file('contents', 'filename.json')
end

def test_write_app_file_success
@mock_driver.expects(:failed?).returns(false)
@mock_driver.expects(:app_id).returns('app1')
Maze::Helper.expects(:get_current_platform).returns('ios')
$logger.expects(:trace).with("Pushing file to '@app1/Documents/filename.json' with contents: contents")
@mock_driver.expects(:push_file).with('@app1/Documents/filename.json', 'contents')

assert_true(@manager.write_app_file('contents', 'filename.json'))
end

def test_write_app_file_failure
@mock_driver.expects(:failed?).returns(false)
@mock_driver.expects(:app_id).returns('app1')
Maze::Helper.expects(:get_current_platform).returns('ios')
$logger.expects(:trace).with("Pushing file to '@app1/Documents/filename.json' with contents: contents")
@mock_driver.expects(:push_file).with('@app1/Documents/filename.json', 'contents').raises(Selenium::WebDriver::Error::UnknownError, 'error')
$logger.expects(:error).with("Error writing file to device: error")

assert_false(@manager.write_app_file('contents', 'filename.json'))
end

def test_read_app_file_failed_driver
@mock_driver.expects(:failed?).returns(true)
$logger.expects(:error).with("Cannot read file from device - Appium driver failed.")

@manager.read_app_file('filename.json')
end

def test_read_app_file_success
@mock_driver.expects(:failed?).returns(false)
@mock_driver.expects(:app_id).returns('app1')
Maze::Helper.expects(:get_current_platform).returns('ios')
$logger.expects(:trace).with("Attempting to read file from '@app1/Documents/filename.json'")
@mock_driver.expects(:pull_file).with('@app1/Documents/filename.json')

assert_true(@manager.read_app_file('filename.json'))
end

def test_read_app_file_failure
@mock_driver.expects(:failed?).returns(false)
@mock_driver.expects(:app_id).returns('app1')
Maze::Helper.expects(:get_current_platform).returns('ios')
$logger.expects(:trace).with("Attempting to read file from '@app1/Documents/filename.json'")
@mock_driver.expects(:pull_file).with('@app1/Documents/filename.json').raises(Selenium::WebDriver::Error::UnknownError, 'error')

$logger.expects(:error).with("Error reading file from device: error")

assert_false(@manager.read_app_file('filename.json'))
end
end
end
end
end
Loading