run_loop 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +22 -0
- data/.gitmodules +3 -0
- data/.irbrc +17 -0
- data/CHANGES.txt +1 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +16 -0
- data/Rakefile +2 -0
- data/bin/run-loop +9 -0
- data/docs/intro.md +17 -0
- data/irb.sh +2 -0
- data/lib/run_loop.rb +2 -0
- data/lib/run_loop/cli.rb +21 -0
- data/lib/run_loop/core.rb +160 -0
- data/lib/run_loop/version.rb +3 -0
- data/run_loop.gemspec +20 -0
- data/scripts/run_dismiss_location.js +97 -0
- data/scripts/run_screenshooter.sh +172 -0
- data/scripts/udidetect +0 -0
- data/scripts/unix_instruments +92 -0
- metadata +83 -0
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
coverage
|
6
|
+
out
|
7
|
+
.irb-history
|
8
|
+
InstalledFiles
|
9
|
+
lib/bundler/man
|
10
|
+
pkg
|
11
|
+
rdoc
|
12
|
+
spec/reports
|
13
|
+
test/tmp
|
14
|
+
test/version_tmp
|
15
|
+
tmp
|
16
|
+
Gemfile.lock
|
17
|
+
.idea
|
18
|
+
|
19
|
+
# YARD artifacts
|
20
|
+
.yardoc
|
21
|
+
_yardoc
|
22
|
+
doc/
|
data/.gitmodules
ADDED
data/.irbrc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'irb/completion'
|
3
|
+
require 'irb/ext/save-history'
|
4
|
+
|
5
|
+
ARGV.concat [ "--readline",
|
6
|
+
"--prompt-mode",
|
7
|
+
"simple" ]
|
8
|
+
|
9
|
+
# 25 entries in the list
|
10
|
+
IRB.conf[:SAVE_HISTORY] = 50
|
11
|
+
|
12
|
+
# Store results in home directory with specified file name
|
13
|
+
IRB.conf[:HISTORY_FILE] = ".irb-history"
|
14
|
+
|
15
|
+
require 'run_loop'
|
16
|
+
|
17
|
+
|
data/CHANGES.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0: Initial release
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
run-loop
|
2
|
+
|
3
|
+
Copyright (c) Karl Krukow (http://www.lesspainful.com). All rights reserved.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
run-loop
|
2
|
+
===================
|
3
|
+
|
4
|
+
Credits
|
5
|
+
=======
|
6
|
+
|
7
|
+
UI Screen Shooter @jonathanpenn for the wrapper around instruments
|
8
|
+
https://github.com/jonathanpenn/ui-screen-shooter
|
9
|
+
|
10
|
+
UI Screen Shooter is licensed under the MIT license.
|
11
|
+
See the LICENSE file for more info.
|
12
|
+
|
13
|
+
|
14
|
+
License
|
15
|
+
=======
|
16
|
+
run-loop is available under the MIT license. See the LICENSE file for more info.
|
data/Rakefile
ADDED
data/bin/run-loop
ADDED
data/docs/intro.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
What?
|
2
|
+
=====
|
3
|
+
Library for launching iOS apps and controlling various aspects of iOS simulator.
|
4
|
+
|
5
|
+
Use instruments tool for launching and expose certain UI Automation functionality via a "run loop" UIA script.
|
6
|
+
|
7
|
+
Must work as both a command line tool and a ruby library.
|
8
|
+
|
9
|
+
|
10
|
+
Usage
|
11
|
+
=====
|
12
|
+
|
13
|
+
* Run an app using a predefined or custom UIA script
|
14
|
+
|
15
|
+
* Set simulator hardware, os, language, locale
|
16
|
+
|
17
|
+
* Reset simulator
|
data/irb.sh
ADDED
data/lib/run_loop.rb
ADDED
data/lib/run_loop/cli.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
module RunLoop
|
5
|
+
class CLI < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
def self.source_root
|
9
|
+
File.join( File.dirname(__FILE__), '..','..','scripts' )
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
desc "example", "example desc"
|
14
|
+
long_desc "Long desc"
|
15
|
+
def example
|
16
|
+
say "example: #{CLI.source_root}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
module RunLoop
|
6
|
+
|
7
|
+
class TimeoutError < RuntimeError
|
8
|
+
end
|
9
|
+
|
10
|
+
module Core
|
11
|
+
|
12
|
+
SCRIPTS_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'scripts'))
|
13
|
+
SCRIPTS = {
|
14
|
+
:dismiss => "run_dismiss_location.js"
|
15
|
+
}
|
16
|
+
|
17
|
+
def self.scripts_path
|
18
|
+
SCRIPTS_PATH
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.script_for_key(key)
|
22
|
+
if SCRIPTS[key].nil?
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
File.join(scripts_path, SCRIPTS[key])
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.run_with_options(options)
|
29
|
+
template = automation_template
|
30
|
+
instruments_path = "instruments"#File.join(scripts_path,"unix_instruments")
|
31
|
+
results_dir = options[:results_dir] || Dir.mktmpdir("run_loop")
|
32
|
+
results_dir_trace = File.join(results_dir,"trace")
|
33
|
+
FileUtils.mkdir_p(results_dir_trace)
|
34
|
+
|
35
|
+
bundle_dir_or_bundle_id = options[:app] || ENV['APP_BUNDLE_PATH']
|
36
|
+
|
37
|
+
unless bundle_dir_or_bundle_id
|
38
|
+
raise "key :app or environment variable APP_BUNDLE_PATH must be specified as path to app bundle (simulator) or bundle id (device)"
|
39
|
+
end
|
40
|
+
|
41
|
+
if File.exist?(bundle_dir_or_bundle_id)
|
42
|
+
#Assume simulator
|
43
|
+
udid = nil
|
44
|
+
else
|
45
|
+
udid = options[:udid]
|
46
|
+
unless udid
|
47
|
+
begin
|
48
|
+
Timeout::timeout(3,TimeoutError) do
|
49
|
+
udid = `#{File.join(scripts_path,'udidetect')}`.chomp
|
50
|
+
end
|
51
|
+
rescue TimeoutError => e
|
52
|
+
|
53
|
+
end
|
54
|
+
unless udid
|
55
|
+
raise "Unable to find connected device."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
if udid
|
63
|
+
instruments_path = "#{instruments_path} -w #{udid}"
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
cmd = [
|
69
|
+
instruments_path,
|
70
|
+
"-D", results_dir_trace,
|
71
|
+
"-t", template,
|
72
|
+
"\"#{bundle_dir_or_bundle_id}\"",
|
73
|
+
"-e", "UIARESULTSPATH", results_dir,
|
74
|
+
"-e", "UIASCRIPT", options[:script],
|
75
|
+
*(options[:instruments_args] || [])
|
76
|
+
]
|
77
|
+
|
78
|
+
pid = fork do
|
79
|
+
log_header("Starting App: #{bundle_dir_or_bundle_id}")
|
80
|
+
cmd_str = cmd.join(" ")
|
81
|
+
if ENV['DEBUG']
|
82
|
+
log(cmd_str)
|
83
|
+
end
|
84
|
+
exec(cmd_str)
|
85
|
+
end
|
86
|
+
|
87
|
+
Process.detach(pid)
|
88
|
+
|
89
|
+
File.open(File.join(results_dir,"run_loop.pid"), "w") do |f|
|
90
|
+
f.write pid
|
91
|
+
end
|
92
|
+
|
93
|
+
return {:pid => pid, :results_dir => results_dir}
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.automation_template
|
97
|
+
xcode_path = `xcode-select -print-path`.chomp
|
98
|
+
automation_bundle = File.expand_path(File.join(xcode_path, "..", "Applications", "Instruments.app", "Contents", "PlugIns", "AutomationInstrument.bundle"))
|
99
|
+
if not File.exist? automation_bundle
|
100
|
+
automation_bundle= File.expand_path(File.join(xcode_path, "Platforms", "iPhoneOS.platform", "Developer", "Library", "Instruments", "PlugIns", "AutomationInstrument.bundle"))
|
101
|
+
raise "Unable to find AutomationInstrument.bundle" if not File.exist? automation_bundle
|
102
|
+
end
|
103
|
+
File.join(automation_bundle, "Contents", "Resources", "Automation.tracetemplate")
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.log(message)
|
107
|
+
puts "#{Time.now } #{message}"
|
108
|
+
$stdout.flush
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.log_header(message)
|
112
|
+
puts "\n\e[#{35}m### #{message} ###\e[0m"
|
113
|
+
$stdout.flush
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.run(options={})
|
119
|
+
script = validate_script(options)
|
120
|
+
options[:script] = script
|
121
|
+
|
122
|
+
Core.run_with_options(options)
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.stop(options)
|
126
|
+
results_dir = options[:results_dir]
|
127
|
+
pid = options[:pid] || IO.read(File.join(results_dir,"run_loop.pid"))
|
128
|
+
dest = options[:out] || Dir.pwd
|
129
|
+
|
130
|
+
if pid
|
131
|
+
Process.kill("HUP",pid.to_i)
|
132
|
+
end
|
133
|
+
|
134
|
+
FileUtils.mkdir_p(dest)
|
135
|
+
pngs = Dir.glob(File.join(results_dir,"Run 1","*.png"))
|
136
|
+
FileUtils.cp(pngs, dest) if pngs and pngs.length > 0
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.validate_script(options)
|
140
|
+
script = options[:script]
|
141
|
+
if script
|
142
|
+
if script.is_a?(Symbol)
|
143
|
+
script = Core.script_for_key(script)
|
144
|
+
unless script
|
145
|
+
raise "Unknown script for symbol: #{options[:script]}. Options: #{Core::SCRIPTS.keys.join(', ')}"
|
146
|
+
end
|
147
|
+
elsif script.is_a?(String)
|
148
|
+
unless File.exist?(script)
|
149
|
+
raise "File does not exist: #{script}"
|
150
|
+
end
|
151
|
+
else
|
152
|
+
raise "Unknown type for :script key: #{options[:script].class}"
|
153
|
+
end
|
154
|
+
else
|
155
|
+
script = Core.script_for_key(:dismiss)
|
156
|
+
end
|
157
|
+
script
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
data/run_loop.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "run_loop/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "run_loop"
|
7
|
+
s.version = RunLoop::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Karl Krukow"]
|
10
|
+
s.email = ["karl@lesspainful.com"]
|
11
|
+
s.homepage = "http://calaba.sh"
|
12
|
+
s.summary = %q{Tools related to running Calabash iOS tests}
|
13
|
+
s.description = %q{calabash-cucumber drives tests for native iOS apps. RunLoop provides a number of tools associated with running Calabash tests.}
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = "run-loop"
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency( "thor" )
|
20
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
var target = UIATarget.localTarget(),
|
2
|
+
screenshot_count = 0;
|
3
|
+
|
4
|
+
function findAlertViewText(alert) {
|
5
|
+
if (!alert) {
|
6
|
+
return false;
|
7
|
+
}
|
8
|
+
var txt = alert.name(),
|
9
|
+
txts;
|
10
|
+
if (txt == null) {
|
11
|
+
txts = alert.staticTexts();
|
12
|
+
if (txts != null && txts.length > 0) {
|
13
|
+
|
14
|
+
txt = txts[0].name();
|
15
|
+
}
|
16
|
+
|
17
|
+
}
|
18
|
+
return txt;
|
19
|
+
}
|
20
|
+
|
21
|
+
function isLocationPrompt(alert) {
|
22
|
+
var exps = [
|
23
|
+
["OK", /vil bruge din aktuelle placering/],
|
24
|
+
["OK", /Would Like to Use Your Current Location/],
|
25
|
+
["Ja", /Darf (?:.)+ Ihren aktuellen Ort verwenden/]
|
26
|
+
],
|
27
|
+
ans, exp,
|
28
|
+
txt;
|
29
|
+
|
30
|
+
txt = findAlertViewText(alert);
|
31
|
+
for (var i = 0; i < exps.length; i++) {
|
32
|
+
ans = exps[i][0];
|
33
|
+
exp = exps[i][1];
|
34
|
+
if (exp.test(txt)) {
|
35
|
+
return ans;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
return false;
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
var alertHandlers = [//run in reverse order of this:
|
43
|
+
isLocationPrompt
|
44
|
+
];
|
45
|
+
|
46
|
+
|
47
|
+
UIATarget.onAlert = function (alert) {
|
48
|
+
var N = alertHandlers.length;
|
49
|
+
while (N--) {
|
50
|
+
if (alertHandlers[i]) {
|
51
|
+
break;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
return true;
|
55
|
+
};
|
56
|
+
|
57
|
+
function performAction(action, data) {
|
58
|
+
UIALogger.logMessage("perform action:" + action);
|
59
|
+
var actionTaken = true;
|
60
|
+
switch (action) {
|
61
|
+
case "setLocation":
|
62
|
+
target.setLocation({"latitude":data.latitude, "longitude":data.longitude});
|
63
|
+
break;
|
64
|
+
case "background":
|
65
|
+
target.deactivateAppForDuration(data.duration);
|
66
|
+
break;
|
67
|
+
case "registerAlertHandler":
|
68
|
+
alertHandlers.push(eval(data.handler));
|
69
|
+
break;
|
70
|
+
case "screenshot":
|
71
|
+
screenshot_count += 1;
|
72
|
+
target.captureScreenWithName(data.name || ("screenshot_" + screenshot_count));
|
73
|
+
break;
|
74
|
+
}
|
75
|
+
if (actionTaken && !data.preserve) {
|
76
|
+
target.frontMostApp().setPreferencesValueForKey(null, "__run_loop_action");
|
77
|
+
}
|
78
|
+
|
79
|
+
}
|
80
|
+
|
81
|
+
UIALogger.logStart("RunLoop");
|
82
|
+
|
83
|
+
var app = target.frontMostApp(),
|
84
|
+
val,
|
85
|
+
count = 0,
|
86
|
+
action = null;
|
87
|
+
while (true) {
|
88
|
+
target.delay(0.3);
|
89
|
+
val = app.preferencesValueForKey("__run_loop_action");
|
90
|
+
if (val && typeof val == 'object') {
|
91
|
+
action = val.action;
|
92
|
+
performAction(action, val);
|
93
|
+
}
|
94
|
+
count += 1;
|
95
|
+
}
|
96
|
+
|
97
|
+
UIALogger.logPass("RunLoop");
|
@@ -0,0 +1,172 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# Copyright (c) 2012 Jonathan Penn (http://cocoamanifest.net/)
|
3
|
+
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
|
22
|
+
|
23
|
+
# Tell bash that we want the whole script to fail if any part fails.
|
24
|
+
set -e
|
25
|
+
|
26
|
+
# We require a parameter for where to put the results
|
27
|
+
destination="$1"
|
28
|
+
|
29
|
+
main() {
|
30
|
+
check_destination
|
31
|
+
|
32
|
+
xcode clean build TARGETED_DEVICE_FAMILY=1
|
33
|
+
|
34
|
+
bin/choose_sim_device "iPhone (Retina 3.5-inch)"
|
35
|
+
shoot en fr ja
|
36
|
+
|
37
|
+
bin/choose_sim_device "iPhone (Retina 4-inch)"
|
38
|
+
shoot en fr ja
|
39
|
+
|
40
|
+
# We to build again with the iPad device family because otherwise Instruments
|
41
|
+
# will build and run for iPhone even though the simulator says otherwise.
|
42
|
+
xcode build TARGETED_DEVICE_FAMILY=2
|
43
|
+
|
44
|
+
bin/choose_sim_device "iPad (Retina)"
|
45
|
+
shoot en fr ja
|
46
|
+
|
47
|
+
close_sim
|
48
|
+
}
|
49
|
+
|
50
|
+
# Global variables to keep track of where everything goes
|
51
|
+
dev_tools_dir=`xcode-select -print-path`
|
52
|
+
tmp_dir="/tmp"
|
53
|
+
build_dir="$tmp_dir/screen_shooter"
|
54
|
+
bundle_dir="$build_dir/app.app"
|
55
|
+
trace_results_dir="$build_dir/traces"
|
56
|
+
|
57
|
+
check_destination() {
|
58
|
+
# Abort if the destination directory already exists. Better safe than sorry.
|
59
|
+
|
60
|
+
if [ -z "$destination" ]; then
|
61
|
+
echo "usage: run_screenshooter.sh destination_directory"
|
62
|
+
exit 1
|
63
|
+
elif [ -d "$destination" ]; then
|
64
|
+
echo "Destination directory \"$destination\" already exists! Aborting."
|
65
|
+
exit 1
|
66
|
+
fi
|
67
|
+
}
|
68
|
+
|
69
|
+
shoot() {
|
70
|
+
# Takes the sim device type and a language code, runs the screenshot script,
|
71
|
+
# and then copies over the screenshots to the destination
|
72
|
+
|
73
|
+
for language in $*; do
|
74
|
+
clean_trace_results_dir
|
75
|
+
choose_sim_language $language
|
76
|
+
run_automation "automation/shoot_the_screens.js"
|
77
|
+
copy_screenshots
|
78
|
+
done
|
79
|
+
}
|
80
|
+
|
81
|
+
xcode() {
|
82
|
+
# A wrapper around `xcodebuild` that tells it to build the app in the temp
|
83
|
+
# directory. If your app uses workspaces or special schemes, you'll need to
|
84
|
+
# specify them here.
|
85
|
+
#
|
86
|
+
# Use `man xcodebuild` for more information on how to build your project.
|
87
|
+
|
88
|
+
xcodebuild -sdk iphonesimulator \
|
89
|
+
CONFIGURATION_BUILD_DIR=$build_dir \
|
90
|
+
PRODUCT_NAME=app \
|
91
|
+
$*
|
92
|
+
}
|
93
|
+
|
94
|
+
clean_trace_results_dir() {
|
95
|
+
# Removes the trace results directory. We need to do this because Instruments
|
96
|
+
# keeps appending new trace runs and it's simpler for us to always assume
|
97
|
+
# there's just one run recorded where we look for screenshots.
|
98
|
+
|
99
|
+
rm -rf "$trace_results_dir"
|
100
|
+
mkdir -p "$trace_results_dir"
|
101
|
+
}
|
102
|
+
|
103
|
+
choose_sim_language() {
|
104
|
+
# Alters the global preference file for every simulator type and moves the
|
105
|
+
# chosen language identifier to the top.
|
106
|
+
|
107
|
+
echo "Localizing for $1"
|
108
|
+
|
109
|
+
pref_files=`ls ~/Library/Application\ Support/iPhone\ Simulator/[0-9]*/Library/Preferences/.GlobalPreferences.plist`
|
110
|
+
|
111
|
+
close_sim
|
112
|
+
|
113
|
+
|
114
|
+
# Set the string split delimiter to a newline for the 'for..in'
|
115
|
+
old=$IFS
|
116
|
+
IFS="
|
117
|
+
"
|
118
|
+
|
119
|
+
for file in $pref_files; do
|
120
|
+
# Disable errors temporarily just in case the prefs don't have the key
|
121
|
+
# we're trying to delete. This could happen when experimenting and leaving
|
122
|
+
# the prefs file in an inconsistent state. If anything goes horribly wrong,
|
123
|
+
# just delete the prefs file altogether and run the simulator to recreate
|
124
|
+
# it.
|
125
|
+
set +e
|
126
|
+
/usr/libexec/PlistBuddy "$file" -c "Delete :AppleLanguages"
|
127
|
+
set -e
|
128
|
+
|
129
|
+
# Create the language array with just the given language
|
130
|
+
/usr/libexec/PlistBuddy "$file" \
|
131
|
+
-c "Add :AppleLanguages array" \
|
132
|
+
-c "Add :AppleLanguages:0 string '$1'" \
|
133
|
+
-c "Set :AppleLocale '$1'"
|
134
|
+
done
|
135
|
+
|
136
|
+
# Put the old string split delimiter back
|
137
|
+
IFS=$old
|
138
|
+
}
|
139
|
+
|
140
|
+
|
141
|
+
close_sim() {
|
142
|
+
# Closes the simulator. We need to do this after altering the languages and
|
143
|
+
# when we want to clean up at the end.
|
144
|
+
|
145
|
+
osascript -e 'tell application "iPhone Simulator" to quit'
|
146
|
+
}
|
147
|
+
|
148
|
+
run_automation() {
|
149
|
+
# Runs the UI Automation JavaScript file that actually takes the screenshots.
|
150
|
+
|
151
|
+
tracetemplate="$dev_tools_dir/../Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate"
|
152
|
+
|
153
|
+
# Check out the `unix_instruments` script to see why we need this wrapper.
|
154
|
+
bin/unix_instruments \
|
155
|
+
-D "$trace_results_dir/trace" \
|
156
|
+
-t "$tracetemplate" \
|
157
|
+
$bundle_dir \
|
158
|
+
-e UIARESULTSPATH "$trace_results_dir" \
|
159
|
+
-e UIASCRIPT "$1" \
|
160
|
+
$*
|
161
|
+
}
|
162
|
+
|
163
|
+
copy_screenshots() {
|
164
|
+
# Since we're always clearing out the trace results before every run, we can
|
165
|
+
# assume that any screenshots were saved in the "Run 1" directory. Copy them
|
166
|
+
# to the destination!
|
167
|
+
|
168
|
+
mkdir -p "$destination"
|
169
|
+
cp $trace_results_dir/Run\ 1/*.png $destination
|
170
|
+
}
|
171
|
+
|
172
|
+
main
|
data/scripts/udidetect
ADDED
Binary file
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
#
|
3
|
+
# Copyright (c) 2012 Jonathan Penn (http://cocoamanifest.net)
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
#
|
23
|
+
|
24
|
+
|
25
|
+
# unix_instruments
|
26
|
+
#
|
27
|
+
# A wrapper around `instruments` that returns a proper unix status code
|
28
|
+
# depending on whether the run failed or not. Alas, Apple's instruments tool
|
29
|
+
# doesn't care about unix status codes, so I must grep for the "Fail:" string
|
30
|
+
# and figure it out myself. As long as the command doesn't output that string
|
31
|
+
# anywhere else inside it, then it should work.
|
32
|
+
#
|
33
|
+
# I use a tee pipe to capture the output and deliver it to stdout
|
34
|
+
#
|
35
|
+
# Author: Jonathan Penn (jonathan@cocoamanifest.net)
|
36
|
+
#
|
37
|
+
|
38
|
+
set -e # Bomb on any script errors
|
39
|
+
|
40
|
+
run_instruments() {
|
41
|
+
# Because instruments buffers it's output if it determines that it is being
|
42
|
+
# piped to another process, we have to use ptys to get around that so that we
|
43
|
+
# can use `tee` to save the output for grepping and print to stdout in real
|
44
|
+
# time at the same time.
|
45
|
+
#
|
46
|
+
# I don't like this because I'm hard coding a tty/pty pair in here. Suggestions
|
47
|
+
# to make this cleaner?
|
48
|
+
|
49
|
+
output=$(mktemp -t unix-instruments)
|
50
|
+
instruments $@ &> /dev/ttyvf & pid_instruments=$!
|
51
|
+
|
52
|
+
# Cat the instruments output to tee which outputs to stdout and saves to
|
53
|
+
# $output at the same time
|
54
|
+
cat < /dev/ptyvf | tee $output
|
55
|
+
|
56
|
+
# Clear the process id we saved when forking instruments so the cleanup
|
57
|
+
# function called on exit knows it doesn't have to kill anything
|
58
|
+
pid_instruments=0
|
59
|
+
|
60
|
+
# Process the instruments output looking for anything that resembles a fail
|
61
|
+
# message
|
62
|
+
cat $output | get_error_status
|
63
|
+
}
|
64
|
+
|
65
|
+
get_error_status() {
|
66
|
+
# Catch "Instruments Trace Error"
|
67
|
+
# Catch "Instruments Usage Error"
|
68
|
+
# Catch "00-00-00 00:00:00 +000 Fail:"
|
69
|
+
# Catch "00-00-00 00:00:00 +000 Error:"
|
70
|
+
# Catch "00-00-00 00:00:00 +000 None: Script threw an uncaught JavaScript error"
|
71
|
+
ruby -e 'exit 1 if STDIN.read =~ /Instruments Usage Error|Instruments Trace Error|^\d+-\d+-\d+ \d+:\d+:\d+ [-+]\d+ (Fail:|Error:|None: Script threw an uncaught JavaScript error)/'
|
72
|
+
}
|
73
|
+
|
74
|
+
trap cleanup_instruments EXIT INT TERM
|
75
|
+
function cleanup_instruments() {
|
76
|
+
# Because we fork instruments in this script, we need to clean up if it's
|
77
|
+
# still running because of an error or the user pressed Ctrl-C
|
78
|
+
if [[ $pid_instruments -gt 0 ]]; then
|
79
|
+
echo "Cleaning up instruments..."
|
80
|
+
kill $pid_instruments
|
81
|
+
fi
|
82
|
+
}
|
83
|
+
|
84
|
+
# Running this file with "----test" will try to parse an error out of whatever
|
85
|
+
# is handed in to it from stdin. Use this method to double check your work if
|
86
|
+
# you need a custom "get_error_status" function above.
|
87
|
+
if [[ $1 == "----test" ]]; then
|
88
|
+
get_error_status
|
89
|
+
else
|
90
|
+
run_instruments $@
|
91
|
+
fi
|
92
|
+
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: run_loop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Karl Krukow
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: calabash-cucumber drives tests for native iOS apps. RunLoop provides
|
31
|
+
a number of tools associated with running Calabash tests.
|
32
|
+
email:
|
33
|
+
- karl@lesspainful.com
|
34
|
+
executables:
|
35
|
+
- run-loop
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- .gitignore
|
40
|
+
- .gitmodules
|
41
|
+
- .irbrc
|
42
|
+
- CHANGES.txt
|
43
|
+
- Gemfile
|
44
|
+
- LICENSE
|
45
|
+
- README.md
|
46
|
+
- Rakefile
|
47
|
+
- bin/run-loop
|
48
|
+
- docs/intro.md
|
49
|
+
- irb.sh
|
50
|
+
- lib/run_loop.rb
|
51
|
+
- lib/run_loop/cli.rb
|
52
|
+
- lib/run_loop/core.rb
|
53
|
+
- lib/run_loop/version.rb
|
54
|
+
- run_loop.gemspec
|
55
|
+
- scripts/run_dismiss_location.js
|
56
|
+
- scripts/run_screenshooter.sh
|
57
|
+
- scripts/udidetect
|
58
|
+
- scripts/unix_instruments
|
59
|
+
homepage: http://calaba.sh
|
60
|
+
licenses: []
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.8.23
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Tools related to running Calabash iOS tests
|
83
|
+
test_files: []
|