run_loop 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ [submodule "udidetect"]
2
+ path = udidetect
3
+ url = https://github.com/vaskas/udidetect.git
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
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in calabash-ios-cucumber.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/run-loop ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'run_loop/cli'
5
+
6
+
7
+ RunLoop::CLI.start
8
+
9
+
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
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ IRBRC=.irbrc irb -I lib
data/lib/run_loop.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'rubygems'
2
+ require 'run_loop/core'
@@ -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
@@ -0,0 +1,3 @@
1
+ module RunLoop
2
+ VERSION = "0.0.1"
3
+ 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: []