UIAutomation test invocation from the command line with Xcode 6 - command-line

Running UIAutomation tests from the command line seems to break often with new Xcode releases (judging by past posts). Having never used the command line scripting for this, I found this post from 2012: Automation Instrument from the Command Line.
The Problem: My command returns without error, without output results and without anything logged to the system console. The simulator does not even launch!
Checking some of the paths for updates (notably, the Automation trace instrument path), I came up with this command. Note the path in the first parameter (it's different than in past Xcode releases):
instruments -t "/Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.xrplugin/Contents/Resources/Automation.tracetemplate"
"/Users/sohail/Library/Developer/CoreSimulator/Devices/7232A640-A9D2-4626-A2AD-37AFFF706718/data/Containers/Bundle/Application/D07FEC4B-76AD-4844-8362-08E771B81053/MyAppName.app"
-e UIASCRIPT "/Users/sohail/source/MyAppName/MyAppNameAutomationTests/TestRunner.js"
-e UIARESULTSPATH "Users/sohail/source/MyAppName/MyAppNameAutomationTests/TestResults"
This might be easier to read by selecting "raw" from this gist.
Of course:
I validated that the path specified to my .app actually exists; it came into being after a successful build and run.
I validated that in my specified output folder ("TestResults") that in fact, nothing got logged.
I validated that the aforementioned TestRunner.js file I specified, could be found at the path specified, and successfully runs in the Automation Instrument interactively with the Instruments app.
I've checked Apple's Xcode6/iOS8 pre-release documentation (login required; see section title, "Executing an Automation Instrument Script from the Command Line"), and nothing jumps out at me as wrong, since I'm just targeting the simulator.
My suspicion
I'm missing some flag or switch somewhere.
Thoughts anyone?

I saw the exact same issue, after explicitly provided -w $DEVICE parameter, my tests finally got started
instruments -t /Applications/Xcode6.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.xrplugin/Contents/Resources/Automation.tracetemplate
-w "iPhone 5s (8.0 Simulator)" /path/to/my/TestApp.app -e UIASCRIPT /tmp/script.js -e UIARESULTSPATH /tmp

Related

Diagnose watch utility

I have a script that runs in the background which uses watch to monitor a directory for changes. This works just fine. However, I had a need for the script which runs the monitoring script to be run as daemon. The daemon is running as the same user, but now watch is returning "1 Various failures."
I suspect that there is some environment variable that is not set right, but there are too many to use trial and error to diagnose the issue. And unfortunately, "Various failures" is not very helpful. Any ideas how I might diagnose this?
The command is
watch -d -t -g ls -l
I think something like the following can be used as a work-around.
diff <(ls -l) <(sleep 1; ls -l)
I finally discovered the following on stderr.
Error opening terminal: unknown.
It was easily resolved by the answer to error opening terminal. So it did turn out to be an environment variable. I don't see this error message in the code. Perhaps it occurs when making a system call that needs the terminal.

Swift 4 launchedProcess launch path not accessible

I am writing a swift based macOS app in Xcode 9 to be used on my computer (not distributed). I have EXIFtool installed (independent of the app) in /usr/local/bin and can use it successfully from the Terminal app. I am trying to access EXIFtool from my app.
My app has a button that when clicked should run the EXIFtool command by executing this script.
#IBAction func arrowClicked(_ sender: Any) {
arrow.isEnabled = false
let task = Process.launchedProcess(launchPath: "/usr/local/bin/exiftool", arguments: [rawURL])
task.waitUntilExit()
arrow.isEnabled = true
}
The script fails with a "launch path not accessible" error. It doesn't matter what I enter as the arguments (in the above snippet, rawURL is a string that contains the path to a user identified image file.
The responses I have found for similar questions here focus on the format of the path (e.g., must be the full path, begin with /, etc). My launch path comes from what Terminal gives a response to "which exiftool", so I thought it was correct.
UPDATE: I followed the link Matt provided and rewrote the code to utilize a shell script. I made the script executable and successfully ran it through Terminal and TextWrangler. But accessing it within Xcode resulted in an "operation not permitted" message.
Turning off the App Sandbox resolves both the original "launch path not accessible" message and the revised attempt's "operation not permitted" message.
There are unfortunately numerous reasons why one binary may not run inside a sandboxed environment, whilst another binary may run just fine.
In your case you were able to disable sandboxing, so that was an easy (and sensible) fix, but for anyone without that luxury, below is some information that documents some relevant factors.
There is a question here which asks why /sbin/ping runs fine, yet /usr/sbin/traceroute does not.
From one of the answers there:
ping vs. traceroute - the former is a non-priviledged program, the latter is priviledged and runs as root
You can see the difference in their permissions:
$ ls /sbin/ping
-r-xr-xr-x 1 root wheel 41K 30 May 11:36 /sbin/ping
$ ls /usr/sbin/traceroute
-r-sr-xr-x 1 root wheel 37K 30 May 11:36 /usr/sbin/traceroute
The s on the trace route means it will be executed as root, which naturally is not going to be allowed inside a sandboxed environment. The following may be helpful, from https://coderanch.com/t/110770/os/permissions-meaning#558594:
"s", for files, means "setuid exec." If a file has s permission, then it's executable, and furthermore, the user id and/or group id of the process is set to the user or group id of the owner of the file, depending on whether it's the user or group "s" that's set. This is a way to give limited root powers to a user -- a program that runs as root when an ordinary user executes it.
Somewhat beyond my comprehension, but potentially also relevant is the following Apple Technical Q&A QA1773 called Common app sandboxing issues which discusses whether a binary is a Mach-O executable:
You can check if a binary is a Mach-O executable using the file command. If any slice of the binary identifies itself as Mach-O executable or Mach-O 64-bit executable, the binary must be sandboxed.
For ping that would look like:
$ file /sbin/ping
/sbin/ping: Mach-O 64-bit executable x86_64
My take is that if the binary is a Mach-O executable and you want to run it from a sandboxed app, you are going to have to either:
compile the binary yourself from source, giving it appropriate entitlements, or
import the code into your app and compile it directly into your app

Can you automate the App UDID when running a test from the command line?

I run a command line test that runs Instruments on the iPhone Simulator. Unfortunately, every time I do a Build the UDID changes which means I have to update my script. E.g. see this error message:
Instruments Usage Error : Specified target process is invalid: /Path/To/Library/Developer/CoreSimulator/Devices/9B0DF4D4-941C-4B65-84E0-ETC/data/Containers/Bundle/Application/8EE5CBCA-3107-48B5-8A54-ETC/myApp.app
Is there any way to avoid copying/pasting this UDID all the time?
You can write a bash script that reads the most recent UDID from the folder where they are being generated, stores that to a variable and calls instruments using that variable. It would look something like
cd parentFolderOfUDIDs
mostRecentUDID = "$(\ls -1dt */ | head -n 1)"
instruments /path/to/app/$mostRecentUDID/myApp.app

Perl debugger on test modules

I'm running into problems testing a new addition to a module. (Specifically - the ~ operator seems to be not working in Math::Complex for this new feature only.) It's too bizarre to be what it appears but the ideal scheme would be to add the -d option on the top line of the .t program.
Well, I was quickly disabused of that idea! It does not invoke the debugger.
If I wanted to use the debugger, I'd need to create an edit of the .t program that:
Uses (the use command) the module directly. not in the form of
BEGIN { use_ok('My::Module') };
Does not "use Test::More;"
A few other edits that cause gluteal pains
The problem with doing that is that any changes I make in the edited test program I still need to transfer back to the true test program use in "make test". Error prone as best.
I am already using "make test TEST_VERBOSE=1" so that my stdio output shows up. But there's GOT to be a simpler way to invoke the debugger on the .t
Thanks for ideas here.
-- JS
use_ok tests are great, but you should have them in test files of their own, not test files that also test other things.
I'm not sure why you would need to avoid Test::More or use_ok to run the debugger, though. What does happen when you try your test directly:
perl -d -Mblib t/yourtestfile.t?
If all else fails, you can try using Enbugger in your test script.

UIAutomation - different results from Instruments and Command Line

When I run a UIAutomation script in Instruments, everything works fine but when I run the exact same script from the command line, I get this error :
Cannot perform action on invalid element: UIAElementNil from target.frontMostApp().mainWindow().tableViews()[0].cells()["ID number, Required"].textFields()[0]
Here is the Instruments command I'm using to launch the test
instruments -t /Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate /Users/johan/Library/Developer/Xcode/DerivedData/Brokers-etvmwznhcjprybdekgtixzzsnbrw/Build/Products/Release-iphonesimulator/MyApp -e UIASCRIPT /Users/johan/Desktop/Script.js
The reason might be that UIAutomation under instruments is much slower than when run from the command line. So it might be that under instruments the element target.frontMostApp().mainWindow().tableViews()[0].cells()["ID number, Required"].textFields()[0]
exists, i.e. is valid, but from the console not yet.
Maybe you should try to wait for the element to become valid, before you perform an action on it, of to check its validity (isValid) and log it to be sure about its status.