run_loop 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []