-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3a8b164
Showing
13 changed files
with
459 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
*.gem | ||
*.rbc | ||
.bundle | ||
.config | ||
.yardoc | ||
Gemfile.lock | ||
InstalledFiles | ||
_yardoc | ||
coverage | ||
doc/ | ||
lib/bundler/man | ||
pkg | ||
rdoc | ||
spec/reports | ||
test/tmp | ||
test/version_tmp | ||
tmp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
source 'https://rubygems.org' | ||
|
||
# Specify your gem's dependencies in uiauto.gemspec | ||
gemspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
Copyright (c) 2013 Mike Enriquez | ||
|
||
MIT License | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
"Software"), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# UIAuto | ||
|
||
UIAuto is a command line tool for running UI Automation scripts. It improves Apple's `instruments` command by assuming reasonable defaults specific to UI Automation. | ||
|
||
UIAuto also facilitates the setup of simulator data for running scripts in a repeatable and known state. | ||
|
||
Note: UIAuto only works with the simulator for now. | ||
|
||
## Prerequisites | ||
|
||
* Xcode command line tools | ||
|
||
## Installation | ||
|
||
$ gem install uiauto | ||
|
||
## Usage | ||
|
||
First, you need to build your app to run on the simulator. By default, `uiauto` will look for the most recently built app bundle in derived data based on your current working directory. UIAuto also provides commands and can read speical comment headers to setup simulator data. More details below. | ||
|
||
### Build your project | ||
|
||
UIAuto does not build your app. You can use Xcode or `xcodebuild`. | ||
|
||
#### With Xcode | ||
|
||
The easiest way to build your app for UIAuto is to use Xcode. Open your app in Xcode, select the simulator, then build your project (command+b). This builds your app and places the resulting bundle in derived data. | ||
|
||
#### With xcodebuild | ||
|
||
You can build from the command line using `xcodebuild`. The following examples show how to build your iOS 6.1 app for the simulator. | ||
|
||
# Example using xcodebuild to build a workspace | ||
$ xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -sdk iphonesimulator6.1 | ||
|
||
# Example using xcodebuild to build a project | ||
$ xcodebuild -project MyApp.xcodeproj -sdk iphonesimulator6.1 | ||
|
||
By building with the commands above, the resulting bundle is placed in derived data. | ||
|
||
### Run UI Automation scripts | ||
|
||
The command to run automation scripts is simplified if the built app is in derived data and the defaults are used. For special use cases, these defaults may be overridden. | ||
|
||
#### Default options | ||
|
||
In the same directory as your project's .xcworkspace or .xcodeproj run the following. | ||
|
||
$ uiauto exec path_to_your_script.js | ||
|
||
This will run `path_to_your_script.js` using the app bundle located in derivated data that you just built. The results and trace file are placed in `./uiauto/results` and `./uiauto/results/trace`. | ||
|
||
#### Advanced options | ||
|
||
Running `uiauto help exec` prints the following message. | ||
|
||
Usage: | ||
uiauto exec [FILE_OR_DIRECTORY] | ||
|
||
Options: | ||
[--results=RESULTS] | ||
|
||
[--trace=TRACE] | ||
|
||
[--app=APP] | ||
|
||
If you build your app outside of derived data, then you can specify the `--app` flag to tell uiauto where to find the `*.app`. You can also override the default locations for the trace file and results. For example, if your build your app in a build directory you can run the following | ||
|
||
uiauto exec uiauto/scripts/script_to_run.js --app=build/MyApp.app | ||
|
||
### Simulator data | ||
|
||
UIAuto's `simulator` subcommand allows you to setup the simulator's applications, settings, and data. This is done by taking a "snapshot" of the simulator's current data by saving the `~/Library/Application Support/iPhone Simulator/(SDK VERSION)/` directory somewhere, then loading it back in when needed. | ||
|
||
This is useful for getting the simulator into a known state before running automation scripts. | ||
|
||
#### uiauto simulator command | ||
|
||
Running `uiauto simulator` prints the following message | ||
|
||
Commands: | ||
uiauto simulator close # Closes the simulator | ||
uiauto simulator help [COMMAND] # Describe subcommands or one specific subcommand | ||
uiauto simulator load DATA # Loads previously saved simulator data | ||
uiauto simulator open # Opens the simulator | ||
uiauto simulator reset # Deletes all applications and settings | ||
uiauto simulator save DATA # Saves simulator data | ||
|
||
#### Script comment headers | ||
|
||
By placing a special comment header at the top of your script, `uiauto exec` will automatically load in a previously saved simulator data dump before running the script. | ||
|
||
Let's say you have a TODO list application. You want to script the delete feature, but the problem is that you need tasks to delete first. With UIAuto, you can add a comment header that loads the simulator with your app with its tasks already there. Therefore, your script can safely make this assumption every time that it is ran. | ||
|
||
The steps below explain how to do this. | ||
|
||
##### 1. Manually add tasks to your app in the simulator | ||
|
||
Get data into your app up to the point where you want your script to start. In this case, you want to add some tasks so that your script can delete them later. | ||
|
||
##### 2. Save the simulator's current state | ||
|
||
$ uiauto simulator save uiauto/simulator_data/with_tasks | ||
|
||
This command will save ALL of the data in the simulator including your application's data (and therefore, it'll save the tasks you just created). It is stored in the directory you specified. | ||
|
||
##### 3. Add the comment header | ||
|
||
Add the following to the top of `uiauto/scripts/delete_tasks.js` | ||
|
||
// setup_simulator_data "../simulator_data/with_tasks" | ||
|
||
Inside the quotes is where you specify the location of the simulator data you want loaded in before running the script. It may be a path relative to the script. | ||
|
||
##### 4. Run the script | ||
|
||
Build if needed, then run: | ||
|
||
$ uiauto exec uiauto/scripts/delete_tasks.js | ||
|
||
This will load in the simulator data that was saved in step 2 before running the script. | ||
|
||
## License | ||
|
||
MIT License. See LICENSE.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
require "bundler/gem_tasks" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/usr/bin/env ruby | ||
|
||
require 'uiauto/cli' | ||
UIAuto::CLI.start |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
require "uiauto/version" | ||
|
||
module UIAuto | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
require 'thor' | ||
require 'uiauto/simulator' | ||
require 'uiauto/runner' | ||
|
||
module UIAuto | ||
class SimulatorCLI < Thor | ||
namespace :simulator | ||
|
||
desc "reset", "Deletes all applications and settings" | ||
method_option :sdk, :default => Simulator::CURRENT_IOS_SDK_VERSION | ||
def reset | ||
simulator = Simulator.new(options[:sdk]) | ||
simulator.reset | ||
end | ||
|
||
desc "load DATA", "Loads previously saved simulator data" | ||
method_option :sdk, :default => Simulator::CURRENT_IOS_SDK_VERSION | ||
def load(data_path) | ||
simulator = Simulator.new(options[:sdk]) | ||
simulator.load(data_path) | ||
end | ||
|
||
desc "save DATA", "Saves simulator data" | ||
method_option :sdk, :default => Simulator::CURRENT_IOS_SDK_VERSION | ||
def save(data_path) | ||
simulator = Simulator.new(options[:sdk]) | ||
simulator.save(data_path) | ||
end | ||
|
||
desc "open", "Opens the simulator" | ||
def open | ||
Simulator.open | ||
end | ||
|
||
desc "close", "Closes the simulator" | ||
def close | ||
Simulator.close | ||
end | ||
end | ||
|
||
class CLI < Thor | ||
desc "exec FILE_OR_DIRECTORY", "Runs the given script or directory of scripts through UI Automation" | ||
method_option :results, :default => File.expand_path("./uiauto/results") | ||
method_option :trace, :default => File.expand_path("./uiauto/results/trace") | ||
method_option :app | ||
def exec(file_or_dir = "./uiauto/scripts/") | ||
Runner.run(file_or_dir, options) | ||
end | ||
|
||
desc "simulator SUBCOMMAND ...ARGS", "manage simulator data" | ||
subcommand "simulator", SimulatorCLI | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
require 'pty' | ||
require 'fileutils' | ||
require 'cfpropertylist' | ||
|
||
module UIAuto | ||
class Instruments | ||
attr_accessor :trace, :app, :results, :script | ||
|
||
def initialize(script, opts = {}) | ||
@script = script | ||
@trace = opts[:trace] | ||
@results = opts[:results] | ||
@app = opts[:app] || default_application | ||
|
||
FileUtils.mkdir_p(@results) unless File.exists?(@results) | ||
end | ||
|
||
def command | ||
command = ["xcrun instruments"] | ||
command << "-D #{@trace}" | ||
command << "-t #{automation_template_location}" | ||
command << @app | ||
command << "-e UIASCRIPT #{@script}" | ||
command << "-e UIARESULTSPATH #{@results}" | ||
|
||
command.join(" ") | ||
end | ||
|
||
def execute | ||
exit_status = 0 | ||
read, write = PTY.open | ||
pid = spawn(command, :in => STDIN, :out => write, :err => write) | ||
write.close | ||
|
||
begin | ||
loop do | ||
buffer = read.readpartial(8192) | ||
lines = buffer.split("\n") | ||
lines.each do |line| | ||
puts line | ||
exit_status = 1 if line =~ /Fail:/ && exit_status != 2 | ||
exit_status = 2 if line =~ /Instruments Usage Error|Instruments Trace Error|^\d+-\d+-\d+ \d+:\d+:\d+ [-+]\d+ (Error:|None: Script threw an uncaught JavaScript error)/ | ||
end | ||
end | ||
rescue EOFError | ||
ensure | ||
read.close | ||
end | ||
|
||
exit_status | ||
end | ||
|
||
protected | ||
|
||
def default_application | ||
current_dir = Dir.pwd | ||
product_directories = Dir.glob(File.join(derived_data_location, "*")) | ||
|
||
matching_directories = product_directories.select do |product_dir| | ||
info_plist_file = File.join(product_dir, "info.plist") | ||
if File.exists?(info_plist_file) | ||
info_plist = CFPropertyList::List.new(:file => info_plist_file) | ||
data = CFPropertyList.native_types(info_plist.value) | ||
current_dir == File.dirname(data["WorkspacePath"]) | ||
else | ||
false | ||
end | ||
end | ||
|
||
# TODO: Add support for running on a device | ||
sorted_matches = matching_directories.sort_by { |dir| File.mtime(dir) } | ||
Dir.glob(File.join(sorted_matches.last, "Build/Products/*-iphonesimulator/*.app")).sort_by { |dir| File.mtime(dir) }.last | ||
end | ||
|
||
def automation_template_location | ||
template = nil | ||
`xcrun instruments -s 2>&1 | grep Automation.tracetemplate`.split("\n").each do |path| | ||
path = path.gsub(/^\s*"|",\s*$/, "") | ||
template = path if File.exists?(path) | ||
break if template | ||
end | ||
template | ||
end | ||
|
||
def derived_data_location | ||
# TODO: Parse ~/Library/Preferences/com.apple.dt.Xcode.plist to find customized location | ||
File.expand_path("~/Library/Developer/Xcode/DerivedData/") | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
require 'uiauto/instruments' | ||
require 'uiauto/simulator' | ||
|
||
module UIAuto | ||
class Runner | ||
def self.run(file_or_dir, options = {}) | ||
exit_status = 0 | ||
if file_or_dir.nil? | ||
exit_status = self.run_one options | ||
elsif File.directory?(file_or_dir) | ||
exit_status = self.run_all(file_or_dir, options) | ||
else | ||
exit_status = self.run_one(file_or_dir, options) | ||
end | ||
|
||
exit exit_status | ||
end | ||
|
||
private | ||
|
||
def self.run_one(script, options) | ||
self.process_comment_header script | ||
instruments = Instruments.new(script, options) | ||
exit_status = instruments.execute | ||
|
||
exit_status | ||
end | ||
|
||
def self.run_all(dir, options) | ||
exit_status = 0 | ||
scripts = Dir.glob(File.join(dir, "*.js")) | ||
scripts.each do |script| | ||
script_exit_status = self.run_one(script, options) | ||
if script_exit_status > exit_status | ||
exit_status = script_exit_status | ||
end | ||
end | ||
|
||
exit_status | ||
end | ||
|
||
def self.process_comment_header(script) | ||
File.open(script) do |file| | ||
if file.readline =~ /\s*\/\/ setup_simulator_data "(.+)"/ | ||
path = $1 | ||
full_path = path | ||
if !path.start_with?("/") | ||
full_path = File.expand_path(File.join(File.dirname(script), path)) | ||
end | ||
|
||
simulator = Simulator.new | ||
simulator.load full_path | ||
end | ||
end | ||
end | ||
|
||
end | ||
end |
Oops, something went wrong.