illuminator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/gem/README.md +37 -0
- data/gem/bin/illuminatorTestRunner.rb +22 -0
- data/gem/lib/illuminator.rb +171 -0
- data/gem/lib/illuminator/argument-parsing.rb +299 -0
- data/gem/lib/illuminator/automation-builder.rb +39 -0
- data/gem/lib/illuminator/automation-runner.rb +589 -0
- data/gem/lib/illuminator/build-artifacts.rb +118 -0
- data/gem/lib/illuminator/device-installer.rb +45 -0
- data/gem/lib/illuminator/host-utils.rb +42 -0
- data/gem/lib/illuminator/instruments-runner.rb +301 -0
- data/gem/lib/illuminator/javascript-runner.rb +98 -0
- data/gem/lib/illuminator/listeners/console-logger.rb +32 -0
- data/gem/lib/illuminator/listeners/full-output.rb +13 -0
- data/gem/lib/illuminator/listeners/instruments-listener.rb +22 -0
- data/gem/lib/illuminator/listeners/intermittent-failure-detector.rb +49 -0
- data/gem/lib/illuminator/listeners/pretty-output.rb +26 -0
- data/gem/lib/illuminator/listeners/saltinel-agent.rb +66 -0
- data/gem/lib/illuminator/listeners/saltinel-listener.rb +26 -0
- data/gem/lib/illuminator/listeners/start-detector.rb +52 -0
- data/gem/lib/illuminator/listeners/stop-detector.rb +46 -0
- data/gem/lib/illuminator/listeners/test-listener.rb +58 -0
- data/gem/lib/illuminator/listeners/trace-error-detector.rb +38 -0
- data/gem/lib/illuminator/options.rb +96 -0
- data/gem/lib/illuminator/resources/IlluminatorGeneratedEnvironment.erb +13 -0
- data/gem/lib/illuminator/resources/IlluminatorGeneratedRunnerForInstruments.erb +19 -0
- data/gem/lib/illuminator/test-definitions.rb +23 -0
- data/gem/lib/illuminator/test-suite.rb +155 -0
- data/gem/lib/illuminator/version.rb +3 -0
- data/gem/lib/illuminator/xcode-builder.rb +144 -0
- data/gem/lib/illuminator/xcode-utils.rb +219 -0
- data/gem/resources/BuildConfiguration.xcconfig +10 -0
- data/gem/resources/js/AppMap.js +767 -0
- data/gem/resources/js/Automator.js +1132 -0
- data/gem/resources/js/Base64.js +142 -0
- data/gem/resources/js/Bridge.js +102 -0
- data/gem/resources/js/Config.js +92 -0
- data/gem/resources/js/Extensions.js +2025 -0
- data/gem/resources/js/Illuminator.js +228 -0
- data/gem/resources/js/Preferences.js +24 -0
- data/gem/resources/scripts/UIAutomationBridge.rb +248 -0
- data/gem/resources/scripts/common.applescript +25 -0
- data/gem/resources/scripts/diff_png.sh +61 -0
- data/gem/resources/scripts/kill_all_sim_processes.sh +17 -0
- data/gem/resources/scripts/plist_to_json.sh +40 -0
- data/gem/resources/scripts/set_hardware_keyboard.applescript +0 -0
- metadata +225 -0
@@ -0,0 +1,228 @@
|
|
1
|
+
/**
|
2
|
+
* Main entry point
|
3
|
+
*
|
4
|
+
* There are no arguments to this function, because it will be controlled by Config arguments
|
5
|
+
* and invoked by testAutomatically.js (a generated file). See the documentation on how to
|
6
|
+
* set up and run Illuminator.
|
7
|
+
*/
|
8
|
+
function IlluminatorIlluminate() {
|
9
|
+
// initial sanity checks
|
10
|
+
assertDesiredSimVersion();
|
11
|
+
|
12
|
+
// send test definitions back to framework
|
13
|
+
if (!host().writeToFile(config.buildArtifacts.automatorScenarioJSON,
|
14
|
+
JSON.stringify(automator.toScenarioObject(false), null, " ")))
|
15
|
+
{
|
16
|
+
throw new IlluminatorSetupException("Could not save necessary build artifact to " +
|
17
|
+
config.buildArtifacts.automatorScenarioJSON);
|
18
|
+
}
|
19
|
+
notifyIlluminatorFramework("Saved scenario definitions to: " + config.buildArtifacts.automatorScenarioJSON);
|
20
|
+
|
21
|
+
// run app-specific callback
|
22
|
+
if (!automator._executeCallback("onInit", {entryPoint: config.entryPoint}, false, false)) return;
|
23
|
+
|
24
|
+
// choose entry point and go
|
25
|
+
switch (config.entryPoint) {
|
26
|
+
|
27
|
+
case "runTestsByName":
|
28
|
+
automator.runNamedScenarios(config.automatorScenarioNames, config.automatorSequenceRandomSeed);
|
29
|
+
break;
|
30
|
+
|
31
|
+
case "runTestsByTag":
|
32
|
+
if (0 == (config.automatorTagsAny.length + config.automatorTagsAll.length + config.automatorTagsNone.length)) {
|
33
|
+
UIALogger.logMessage("No tag sets (any / all / none) were specified, so printing some information about defined scenarios");
|
34
|
+
automator.logInfo();
|
35
|
+
notifyIlluminatorFramework("Successful launch");
|
36
|
+
} else {
|
37
|
+
automator.runTaggedScenarios(config.automatorTagsAny,
|
38
|
+
config.automatorTagsAll,
|
39
|
+
config.automatorTagsNone,
|
40
|
+
config.automatorSequenceRandomSeed);
|
41
|
+
}
|
42
|
+
break;
|
43
|
+
|
44
|
+
case "describe":
|
45
|
+
notifyIlluminatorFramework("Successful launch");
|
46
|
+
var appMapMarkdownPath = config.buildArtifacts.appMapMarkdown;
|
47
|
+
var automatorMarkdownPath = config.buildArtifacts.automatorMarkdown;
|
48
|
+
var automatorJSONPath = config.buildArtifacts.automatorJSON;
|
49
|
+
host().writeToFile(appMapMarkdownPath, appmap.toMarkdown());
|
50
|
+
UIALogger.logMessage("Wrote AppMap definitions to " + appMapMarkdownPath);
|
51
|
+
host().writeToFile(automatorMarkdownPath, automator.toMarkdown());
|
52
|
+
UIALogger.logMessage("Wrote automator definitions to " + automatorMarkdownPath);
|
53
|
+
host().writeToFile(automatorJSONPath, JSON.stringify(automator.toScenarioObject(true), null, " "));
|
54
|
+
UIALogger.logMessage("Wrote automator definition data to " + automatorJSONPath);
|
55
|
+
break;
|
56
|
+
|
57
|
+
default:
|
58
|
+
notifyIlluminatorFramework("Successful launch");
|
59
|
+
throw new IlluminatorSetupException("Unknown Illuminator entry point specified: " + config.entryPoint);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Send a message back to the log analyzers (ruby)
|
66
|
+
*
|
67
|
+
* @param message string
|
68
|
+
*/
|
69
|
+
function notifyIlluminatorFramework(message) {
|
70
|
+
UIALogger.logDebug(config.saltinel + " " + message + " " + config.saltinel);
|
71
|
+
}
|
72
|
+
|
73
|
+
function isMatchingVersion(input, prefix, major, minor, rev) {
|
74
|
+
var findStr = prefix + major;
|
75
|
+
|
76
|
+
if (undefined !== minor) {
|
77
|
+
findStr += "." + minor;
|
78
|
+
if (undefined !== rev) {
|
79
|
+
findStr += "." + rev;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
return input.indexOf(findStr) > -1;
|
84
|
+
}
|
85
|
+
|
86
|
+
function isSimVersion(major, minor, rev) {
|
87
|
+
return isMatchingVersion(target().systemVersion(), "", major, minor, rev);
|
88
|
+
}
|
89
|
+
|
90
|
+
function assertDesiredSimVersion() {
|
91
|
+
var ver = target().systemVersion();
|
92
|
+
if (("iOS " + ver).indexOf(config.automatorDesiredSimVersion) == -1) {
|
93
|
+
throw new IlluminatorSetupException("Simulator version " + ver + " is running, but generated-config.js " +
|
94
|
+
"specifies " + config.automatorDesiredSimVersion);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
function actionCompareScreenshotToMaster(parm) {
|
99
|
+
var masterPath = parm.masterPath;
|
100
|
+
var maskPath = parm.maskPath;
|
101
|
+
var captureTitle = parm.captureTitle;
|
102
|
+
var delayCapture = parm.delay === undefined ? 0.4 : parm.delay;
|
103
|
+
|
104
|
+
delay(delayCapture); // wait for any animations to settle
|
105
|
+
|
106
|
+
var diff_pngPath = IlluminatorScriptsDirectory + "/diff_png.sh";
|
107
|
+
UIATarget.localTarget().captureScreenWithName(captureTitle);
|
108
|
+
|
109
|
+
var screenshotFile = captureTitle + ".png";
|
110
|
+
var screenshotPath = config.screenshotDir + "/" + screenshotFile;
|
111
|
+
var compareFileBase = config.screenshotDir + "/compared_" + captureTitle;
|
112
|
+
|
113
|
+
var output = target().host().performTaskWithPathArgumentsTimeout("/bin/sh",
|
114
|
+
[diff_pngPath,
|
115
|
+
masterPath,
|
116
|
+
screenshotPath,
|
117
|
+
maskPath,
|
118
|
+
compareFileBase],
|
119
|
+
20);
|
120
|
+
|
121
|
+
// turn the output into key/value pairs separated by ":"
|
122
|
+
var outputArr = output.stdout.split("\n");
|
123
|
+
var outputObj = {};
|
124
|
+
for (var i = 0; i < outputArr.length; ++i) {
|
125
|
+
var sp = outputArr[i].split(": ", 2)
|
126
|
+
if (sp.length == 2) {
|
127
|
+
outputObj[sp[0]] = sp[1];
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
// sanity check
|
132
|
+
if (!outputObj["pixels changed"]) {
|
133
|
+
throw new IlluminatorRuntimeVerificationException("actionCompareScreenshotToMaster: diff_png.sh failed to produce 'pixels changed' output");
|
134
|
+
}
|
135
|
+
|
136
|
+
// if differences are outside tolerances, throw errors
|
137
|
+
var allPixels = parseInt(outputObj["pixels (total)"]);
|
138
|
+
var wrongPixels = parseInt(outputObj["pixels changed"]);
|
139
|
+
|
140
|
+
var allowedPixels = parm.allowedPixels === undefined ? 0 : parm.allowedPixels;
|
141
|
+
var errmsg = "";
|
142
|
+
if (allowedPixels < wrongPixels) {
|
143
|
+
errmsg = ["Screenshot differed from", masterPath,
|
144
|
+
"by", wrongPixels, "pixels. ",
|
145
|
+
"Comparison image saved to:", compareFileBase + ".png",
|
146
|
+
" and comparison animation saved to:", compareFileBase + ".gif"].join(" ");
|
147
|
+
|
148
|
+
if (parm.deferFailure === true) {
|
149
|
+
automator.deferFailure(errmsg);
|
150
|
+
} else {
|
151
|
+
throw new IlluminatorRuntimeVerificationException(errmsg);
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
if (parm.allowedPercent !== undefined) {
|
156
|
+
var wrongPct = 100.0 * wrongPixels / allPixels;
|
157
|
+
if (wrongPct > parm.allowedPercent) {
|
158
|
+
errmsg = ["Screenshot differed from", masterPath,
|
159
|
+
"by", wrongPct, "%. ",
|
160
|
+
"Comparison image saved to:", compareFileBase + ".png",
|
161
|
+
" and comparison animation saved to:", compareFileBase + ".gif"].join(" ");
|
162
|
+
|
163
|
+
if (parm.deferFailure === true) {
|
164
|
+
} else {
|
165
|
+
throw new IlluminatorRuntimeVerificationException(errmsg);
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
function actionLogAccessors(parm) {
|
172
|
+
if (parm !== undefined && parm.delay !== undefined) {
|
173
|
+
delay(parm.delay);
|
174
|
+
}
|
175
|
+
var visibleOnly = parm !== undefined && parm.visibleOnly === true;
|
176
|
+
UIALogger.logDebug(target().elementReferenceDump("target", visibleOnly));
|
177
|
+
}
|
178
|
+
|
179
|
+
function actionCaptureElementTree(parm) {
|
180
|
+
target().captureImageTree(parm.imageBaseName);
|
181
|
+
}
|
182
|
+
|
183
|
+
|
184
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
185
|
+
// Appmap additions - common capabilities
|
186
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
187
|
+
appmap.createOrAugmentApp("Illuminator").withScreen("do")
|
188
|
+
.onTarget(config.implementation, function() { return true; })
|
189
|
+
// onTarget(config.implementation...) is a HACK.
|
190
|
+
// Illuminator doesn't define implementations, so we use what we're given.
|
191
|
+
// (i.e. you shouldn't copy/paste this example into your project code)
|
192
|
+
|
193
|
+
.withAction("delay", "Delay a given amount of time")
|
194
|
+
.withParam("seconds", "Number of seconds to delay", true, true)
|
195
|
+
.withImplementation(function(parm) {delay(parm.seconds);})
|
196
|
+
|
197
|
+
.withAction("debug", "Print the results of a debug function")
|
198
|
+
.withParam("debug_fn", "Function returning a string", true)
|
199
|
+
.withImplementation(function(parm) { UIALogger.logMessage(parm.debug_fn()); })
|
200
|
+
|
201
|
+
.withAction("logTree", "Log the UI element tree")
|
202
|
+
.withImplementation(function() { UIATarget.localTarget().logElementTree(); })
|
203
|
+
|
204
|
+
.withAction("logAccessors", "Log the list of valid element accessors")
|
205
|
+
.withParam("visibleOnly", "Whether to log only the visible elements", false, true)
|
206
|
+
.withParam("delay", "Number of seconds to delay before logging", false, true)
|
207
|
+
.withImplementation(actionLogAccessors)
|
208
|
+
|
209
|
+
.withAction("captureElementTree", "Take individual screenshots of all screen elements")
|
210
|
+
.withParam("imageBaseName", "The base name for the image files", true, true)
|
211
|
+
.withImplementation(actionCaptureElementTree)
|
212
|
+
|
213
|
+
.withAction("fail", "Unconditionally fail the current test for debugging purposes")
|
214
|
+
.withImplementation(function() { throw new IlluminatorRuntimeVerificationException("purposely-thrown exception to halt the test scenario"); })
|
215
|
+
|
216
|
+
.withAction("verifyScreenshot", "Validate a screenshot against a png template of the expected view")
|
217
|
+
.withParam("masterPath", "The path to the file that is considered the 'expected' view", true, true)
|
218
|
+
.withParam("maskPath", "The path to the file that masks variable portions of the 'expected' view", true, true)
|
219
|
+
.withParam("captureTitle", "The title of the screenshot to capture", true, true)
|
220
|
+
.withParam("delay", "The amount of time to delay before taking the screenshot", false, true)
|
221
|
+
.withParam("allowedPixels", "The maximum number of pixels that are allowed to differ (default 0)", false, true)
|
222
|
+
.withParam("allowedPercent", "The maximum percentage of pixels that are allowed to differ (default 0)", false, true)
|
223
|
+
.withParam("deferFailure", "Whether to defer a failure until the end of the test", false, true)
|
224
|
+
.withImplementation(actionCompareScreenshotToMaster)
|
225
|
+
|
226
|
+
.withAction("testAsAction", "Execute an entire test as one action")
|
227
|
+
.withParam("test", "The function that performs the entire test")
|
228
|
+
.withImplementation(function (parm) { parm.test(); });
|
@@ -0,0 +1,24 @@
|
|
1
|
+
// Preferences.js
|
2
|
+
//
|
3
|
+
// Provides the set of known user preferences for Illuminator
|
4
|
+
|
5
|
+
|
6
|
+
(function() {
|
7
|
+
|
8
|
+
var root = this,
|
9
|
+
preferences = null;
|
10
|
+
|
11
|
+
// put preferences in namespace of importing code
|
12
|
+
if (typeof exports !== 'undefined') {
|
13
|
+
preferences = exports;
|
14
|
+
} else {
|
15
|
+
preferences = root.preferences = {};
|
16
|
+
}
|
17
|
+
|
18
|
+
preferences.extensions = {};
|
19
|
+
preferences.extensions.reduceTimeout = 10; // seconds
|
20
|
+
|
21
|
+
preferences.automator = {};
|
22
|
+
preferences.automator.onError = {};
|
23
|
+
|
24
|
+
}).call(this);
|
@@ -0,0 +1,248 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'json'
|
4
|
+
require 'base64'
|
5
|
+
require 'dnssd'
|
6
|
+
require 'socket'
|
7
|
+
require 'timeout'
|
8
|
+
require 'optparse'
|
9
|
+
|
10
|
+
# all parsing code goes here
|
11
|
+
def parse_arguments(args)
|
12
|
+
ret = {}
|
13
|
+
ret["timeout"] = 15
|
14
|
+
op = OptionParser.new do |opts|
|
15
|
+
opts.banner = "Usage: #{__FILE__} [options]"
|
16
|
+
opts.separator ""
|
17
|
+
opts.separator "Specific options:"
|
18
|
+
|
19
|
+
opts.on("-a", "--argument=[JSON]",
|
20
|
+
"Pass the supplied JSON data over the bridge") do |v|
|
21
|
+
ret["argument"] = v
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("-b", "--b64argument=[B64-JSON]",
|
25
|
+
"Pass the base64-encoded JSON data over the bridge") do |v|
|
26
|
+
ret["b64argument"] = v
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-s", "--selector=SELECTOR",
|
30
|
+
"Call the given function (selector) via the bridge") do |v|
|
31
|
+
ret["selector"] = v
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("-c", "--callUID=UID",
|
35
|
+
"Use the given UID to properly identify the return value of this call") do |v|
|
36
|
+
ret["callUID"] = v
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-r", "--hardwareID=[HARDWAREID]",
|
40
|
+
"If provided, connect to the physical iOS device with this hardware ID instead of a simulator") do |v|
|
41
|
+
ret["hardwareID"] = v
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("-t", "--timeout=[TIMEOUT]",
|
45
|
+
"The timeout in seconds for reading a response from the bridge (default 15)") do |v|
|
46
|
+
ret["timeout"] = v.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
50
|
+
puts opts
|
51
|
+
exit
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
op.parse!(args)
|
56
|
+
return ret
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# communicate the result to the console
|
61
|
+
# success: boolean whether it all went well
|
62
|
+
# failmsg: what to say went wrong
|
63
|
+
# checkpoints: some stuff to print out, debug info
|
64
|
+
# output_data: the actual results
|
65
|
+
def print_result_and_exit(success, failmsg, checkpoints, response=nil)
|
66
|
+
output_data = {}
|
67
|
+
output_data["checkpoints"] = checkpoints
|
68
|
+
output_data["response"] = response unless response.nil?
|
69
|
+
output_data["success"] = success
|
70
|
+
output_data["message"] = failmsg
|
71
|
+
puts JSON.pretty_generate(output_data)
|
72
|
+
exit(success ? 0 : 1)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def get_host_port_of_hardware_id(hardware_id, timeout_seconds)
|
77
|
+
service = DNSSD::Service.new
|
78
|
+
host, port = nil, nil
|
79
|
+
begin
|
80
|
+
Timeout::timeout(timeout_seconds) do
|
81
|
+
service.browse '_bridge._tcp' do |result|
|
82
|
+
if result.name.eql? "UIAutomationBridge_#{hardware_id}"
|
83
|
+
resolver = DNSSD::Service.new
|
84
|
+
resolver.resolve result do |r|
|
85
|
+
host = r.target
|
86
|
+
port = r.port
|
87
|
+
break unless r.flags.more_coming?
|
88
|
+
end
|
89
|
+
break
|
90
|
+
end
|
91
|
+
end
|
92
|
+
return host, port
|
93
|
+
end
|
94
|
+
rescue Timeout::Error
|
95
|
+
end
|
96
|
+
return host, port
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def connect(host, port, timeout=5)
|
101
|
+
addr = Socket.getaddrinfo(host, nil)
|
102
|
+
sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
103
|
+
|
104
|
+
if timeout
|
105
|
+
secs = Integer(timeout)
|
106
|
+
usecs = Integer((timeout - secs) * 1_000_000)
|
107
|
+
optval = [secs, usecs].pack('l_2')
|
108
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
109
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
110
|
+
end
|
111
|
+
begin
|
112
|
+
sock.connect(Socket.pack_sockaddr_in(port, addr[0][3]))
|
113
|
+
return sock, ""
|
114
|
+
rescue Exception => e
|
115
|
+
return nil, "Failed to connect to #{host}:#{port} - #{e.class.name} - #{e.message}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
# these describe what we expect to have happen, and we will check them off as they do
|
121
|
+
checkpoints = {}
|
122
|
+
checkpoints["ruby"] = true
|
123
|
+
checkpoints["argument"] = nil
|
124
|
+
checkpoints["hardwareID"] = nil
|
125
|
+
checkpoints["connection"] = nil
|
126
|
+
checkpoints["request"] = nil
|
127
|
+
checkpoints["response"] = nil
|
128
|
+
checkpoints["callUIDMatch"] = nil
|
129
|
+
|
130
|
+
|
131
|
+
# process the input
|
132
|
+
options = parse_arguments(ARGV)
|
133
|
+
|
134
|
+
# verify that the input selector was provided
|
135
|
+
if options["selector"].nil?
|
136
|
+
print_result_and_exit(false, "selector not provided", checkpoints)
|
137
|
+
end
|
138
|
+
|
139
|
+
# decode b64 argument if provided
|
140
|
+
unless options["b64argument"].nil?
|
141
|
+
begin
|
142
|
+
decoded_arg = Base64.strict_decode64(options["b64argument"])
|
143
|
+
options['argument'] = decoded_arg # the next if block will find & process this
|
144
|
+
JSON.parse(decoded_arg)
|
145
|
+
rescue ArgumentError => e
|
146
|
+
print_result_and_exit(false, "Error decoding b64argument: #{e.message}", checkpoints)
|
147
|
+
rescue JSON::ParserError => e
|
148
|
+
print_result_and_exit(false, "Decoded b64argument does not appear to contain (valid) JSON", checkpoints)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# parse JSON in argument if provided (or b64 provided)
|
153
|
+
unless options["argument"].nil?
|
154
|
+
checkpoints["argument"] = false
|
155
|
+
begin
|
156
|
+
parsed_arg = JSON.parse(options["argument"])
|
157
|
+
options["jsonArgument"] = parsed_arg
|
158
|
+
checkpoints["argument"] = true
|
159
|
+
rescue JSON::ParserError => e
|
160
|
+
print_result_and_exit(false, "Error parsing JSON argument: #{e.message}", checkpoints)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# build the request that will go the server
|
165
|
+
request_hash = {}
|
166
|
+
request_hash['argument'] = options["jsonArgument"] unless options["jsonArgument"].nil?
|
167
|
+
request_hash["selector"] = options["selector"]
|
168
|
+
request_hash["callUID"] = options["callUID"]
|
169
|
+
request = request_hash.to_json
|
170
|
+
checkpoints["actual_request"] = request_hash
|
171
|
+
|
172
|
+
# get the host/port according to whether we are using hardware
|
173
|
+
host, port = '127.0.0.1', 4200
|
174
|
+
unless options["hardwareID"].nil?
|
175
|
+
checkpoints["hardwareID"] = false
|
176
|
+
host, port = get_host_port_of_hardware_id(options["hardwareID"], 3)
|
177
|
+
if host.nil? or port.nil?
|
178
|
+
print_result_and_exit(false, "Failed to get host/port for hardware ID", checkpoints)
|
179
|
+
end
|
180
|
+
checkpoints["hardwareID"] = true
|
181
|
+
end
|
182
|
+
|
183
|
+
# connect
|
184
|
+
checkpoints["host"] = host
|
185
|
+
checkpoints["port"] = port
|
186
|
+
checkpoints["connection"] = false
|
187
|
+
socket_stream, err_message = connect host, port
|
188
|
+
if socket_stream.nil?
|
189
|
+
print_result_and_exit(false, err_message, checkpoints)
|
190
|
+
end
|
191
|
+
checkpoints["connection"] = true
|
192
|
+
|
193
|
+
begin
|
194
|
+
# send request
|
195
|
+
checkpoints["request"] = false
|
196
|
+
socket_stream.write(request)
|
197
|
+
|
198
|
+
checkpoints["request"] = true
|
199
|
+
|
200
|
+
# read response
|
201
|
+
checkpoints["response"] = false
|
202
|
+
response = ''
|
203
|
+
|
204
|
+
timeout(options["timeout"]) do
|
205
|
+
while true
|
206
|
+
new_data = nil
|
207
|
+
begin
|
208
|
+
timeout(0.1) do
|
209
|
+
new_data = socket_stream.gets("}") # read up to closing brace at a time
|
210
|
+
end
|
211
|
+
rescue Timeout::Error
|
212
|
+
# nothing
|
213
|
+
end
|
214
|
+
|
215
|
+
unless new_data.nil?
|
216
|
+
response = response + new_data
|
217
|
+
end
|
218
|
+
|
219
|
+
begin
|
220
|
+
# successfully parse, or go back and try again
|
221
|
+
JSON.parse(response)
|
222
|
+
break
|
223
|
+
rescue
|
224
|
+
# nothing
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
checkpoints["response"] = true
|
229
|
+
checkpoints["response_length"] = response.length
|
230
|
+
|
231
|
+
rescue Timeout::Error
|
232
|
+
print_result_and_exit(false, "Timed out waiting for response", checkpoints)
|
233
|
+
rescue Exception => e
|
234
|
+
print_result_and_exit(false, "Error while waiting for response: #{e.inspect()}", checkpoints)
|
235
|
+
ensure
|
236
|
+
socket_stream.close
|
237
|
+
end
|
238
|
+
|
239
|
+
resp = JSON.parse(response)
|
240
|
+
|
241
|
+
# check callUID
|
242
|
+
checkpoints["callUIDMatch"] = false
|
243
|
+
if options["callUID"] != resp["callUID"]
|
244
|
+
print_result_and_exit(false, "Expected callUID=#{options["callUID"]} but got callUID=#{resp["callUID"]}", checkpoints, resp)
|
245
|
+
end
|
246
|
+
checkpoints["callUIDMatch"] = true
|
247
|
+
|
248
|
+
print_result_and_exit(true, "all bridge options completed successfully", checkpoints, resp)
|