org.uiautomation.ios.server.application.APPIOSApplication Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2012-2013 eBay Software Foundation and ios-driver committers
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.uiautomation.ios.server.application;
import com.dd.plist.ASCIIPropertyListParser;
import com.dd.plist.BinaryPropertyListParser;
import com.dd.plist.BinaryPropertyListWriter;
import com.dd.plist.NSArray;
import com.dd.plist.NSDictionary;
import com.dd.plist.NSNumber;
import com.dd.plist.NSObject;
import com.dd.plist.PropertyListParser;
import com.dd.plist.XMLPropertyListParser;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.selenium.SessionNotCreatedException;
import org.openqa.selenium.WebDriverException;
import org.uiautomation.ios.IOSCapabilities;
import org.uiautomation.ios.communication.device.DeviceType;
import org.uiautomation.ios.server.utils.ZipUtils;
import org.uiautomation.ios.utils.PlistFileUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import static org.uiautomation.ios.IOSCapabilities.*;
// TODO freynaud create IOSApp vs Running App that has locale + language
public class APPIOSApplication {
private static final Logger log = Logger.getLogger(APPIOSApplication.class.getName());
private final JSONObject metadata;
private final File app;
private final List dictionaries = new ArrayList();
/**
* @param pathToApp
* @throws WebDriverException
*/
public APPIOSApplication(String pathToApp) {
this.app = new File(pathToApp);
if (!app.exists()) {
throw new WebDriverException(pathToApp + "isn't an IOS app.");
}
loadAllContent();
try {
metadata = getFullPlist();
} catch (Exception e) {
throw new WebDriverException(
"cannot load the metadata from the Info.plist file for " + pathToApp);
}
}
public String toString() {
return ".APP:" + getApplicationPath().getAbsolutePath();
}
/**
* the content of the Info.plist for the app, as a json object.
*/
public JSONObject getMetadata() {
return metadata;
}
public List getSupportedLanguagesCodes() {
List list = getSupportedLanguages();
List res = new ArrayList();
for (AppleLanguage lang : list) {
res.add(lang.getIsoCode());
}
return res;
}
/**
* get the list of languages the application if localized to.
*/
List getSupportedLanguages() {
if (dictionaries.isEmpty()) {
loadAllContent();
}
/*
* Set res = new HashSet(); List l10ns =
* LanguageDictionary.getL10NFiles(app); for (File f : l10ns) { String name
* = LanguageDictionary.extractLanguageName(f); res.add(new
* LanguageDictionary(name).getLanguage()); } return new
* ArrayList(res);
*/
List res = new ArrayList();
for (LanguageDictionary dict : dictionaries) {
res.add(dict.getLanguage());
}
return res;
}
public AppleLanguage getLanguage(String languageCode) {
if (getSupportedLanguages().isEmpty()) {
return AppleLanguage.emptyLocale(languageCode);
}
if (languageCode == null) {
// default to english if none specified
languageCode = "en";
}
for (AppleLanguage loc : getSupportedLanguages()) {
if (languageCode.equals(loc.getIsoCode())) {
return loc;
}
}
throw new WebDriverException("Cannot find AppleLocale for " + languageCode);
}
public LanguageDictionary getDictionary(String languageCode) throws WebDriverException {
return getDictionary(AppleLanguage.valueOf(languageCode));
}
public LanguageDictionary getDictionary(AppleLanguage language) throws WebDriverException {
if (!language.exists()) {
throw new WebDriverException("The application doesn't have any content files.The l10n "
+ "features cannot be used.");
}
for (LanguageDictionary dict : dictionaries) {
if (dict.getLanguage() == language) {
return dict;
}
}
throw new WebDriverException("Cannot find dictionary for " + language);
}
/**
* Load all the dictionaries for the application.
*/
private void loadAllContent() throws WebDriverException {
if (!dictionaries.isEmpty()) {
throw new WebDriverException("Content already present.");
}
Map dicts = new HashMap();
List l10nFiles = LanguageDictionary.getL10NFiles(app);
for (File f : l10nFiles) {
String name = LanguageDictionary.extractLanguageName(f);
LanguageDictionary res = dicts.get(name);
if (res == null) {
res = new LanguageDictionary(name);
dicts.put(name, res);
}
try {
// and load the content.
JSONObject content = res.readContentFromBinaryFile(f);
res.addJSONContent(content);
} catch (Exception e) {
throw new WebDriverException("error loading content for l10n", e);
}
}
dictionaries.addAll(dicts.values());
}
public String translate(ContentResult res, AppleLanguage language) throws WebDriverException {
LanguageDictionary destinationLanguage = getDictionary(language);
return destinationLanguage.translate(res);
}
public void addDictionary(LanguageDictionary dict) {
dictionaries.add(dict);
}
public String getBundleId() {
return getMetadata("CFBundleIdentifier");
}
public File getApplicationPath() {
return app;
}
/**
* the list of resources to publish via http.
*/
public Map getResources() {
Map resourceByResourceName = new HashMap();
String metadata = getMetadata(ICON);
if(metadata.equals("")){
metadata = getFirstIconFile(BUNDLE_ICONS);
}
resourceByResourceName.put(ICON, metadata);
return resourceByResourceName;
}
private String getFirstIconFile(String bundleIcons){
if(!metadata.has(bundleIcons)){
return "";
}
try{
HashMap icons = (HashMap)metadata.get(bundleIcons);
HashMap primaryIcon = (HashMap)icons.get("CFBundlePrimaryIcon");
ArrayList iconFiles = (ArrayList)primaryIcon.get("CFBundleIconFiles");
return iconFiles.get(0).toString();
}
catch (JSONException e) {
throw new WebDriverException("property 'CFBundleIcons' can't be returned. " + e.getMessage(), e);
}
}
private JSONObject getFullPlist() throws Exception {
File plist = new File(app, "Info.plist");
PlistFileUtils util = new PlistFileUtils(plist);
return util.toJSON();
}
public String getMetadata(String key) {
if (!metadata.has(key)) {
return "";
// throw new WebDriverException("no property " + key +
// " for this app.");
}
try {
return metadata.getString(key);
} catch (JSONException e) {
throw new WebDriverException("property " + key + " can't be returned. " + e.getMessage(), e);
}
}
public List getDeviceFamily() {
try {
JSONArray array = metadata.getJSONArray(DEVICE_FAMILLY);
List res = new ArrayList();
for (int i = 0; i < array.length(); i++) {
res.add(array.getInt(i));
}
return res;
} catch (JSONException e) {
throw new WebDriverException("Cannot load device family", e);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((app == null) ? 0 : app.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
APPIOSApplication other = (APPIOSApplication) obj;
if (app == null) {
if (other.app != null) {
return false;
}
} else if (!app.equals(other.app)) {
return false;
}
return true;
}
public static APPIOSApplication findSafariLocation(File xcodeInstall, String sdkVersion) {
File app = new File(xcodeInstall,
"/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator"
+ sdkVersion
+ ".sdk/Applications/MobileSafari.app");
if (!app.exists()) {
throw new WebDriverException(app + " should be the safari app, but doesn't exist.");
}
return new APPIOSApplication(app.getAbsolutePath());
}
public void setDefaultDevice(DeviceType device) {
try {
File plist = new File(app, "Info.plist");
PListFormat format = getFormat(plist);
NSDictionary root = (NSDictionary) PropertyListParser.parse(new FileInputStream(plist));
NSArray devices = (NSArray) root.objectForKey("UIDeviceFamily");
int length = devices.getArray().length;
if (length == 1) {
return;
}
NSArray rearrangedArray = new NSArray(length);
NSNumber last = null;
int index = 0;
for (int i = 0; i < length; i++) {
NSNumber d = (NSNumber) devices.objectAtIndex(i);
if (d.intValue() == device.getDeviceFamily()) {
last = d;
} else {
rearrangedArray.setValue(index, d);
index++;
}
}
if (last == null) {
throw new WebDriverException(
"Cannot find device " + device + " in the supported device list.");
}
rearrangedArray.setValue(index, last);
root.put("UIDeviceFamily", rearrangedArray);
write(plist,root,format);
} catch (Exception e) {
throw new WebDriverException("Cannot change the default device for the app." + e.getMessage(),
e);
}
}
enum PListFormat{
binary,text,xml;
}
private void write(File dest,NSDictionary content,PListFormat format) throws IOException {
switch (format){
case binary:
BinaryPropertyListWriter.write(dest,content);
case xml:
PropertyListParser.saveAsXML(content,dest);
case text:
PropertyListParser.saveAsASCII(content,dest);
}
}
private PListFormat getFormat(File f) throws IOException {
FileInputStream fis = new FileInputStream(f);
byte b[] = new byte[8];
fis.read(b,0,8);
String magicString = new String(b);
fis.close();
if (magicString.startsWith("bplist")) {
return PListFormat.binary;
} else if (magicString.trim().startsWith("(") || magicString.trim().startsWith("{") || magicString.trim().startsWith("/")) {
return PListFormat.text;
} else {
return PListFormat.xml;
}
}
public IOSCapabilities getCapabilities() {
IOSCapabilities cap = new IOSCapabilities();
cap.setSupportedLanguages(getSupportedLanguagesCodes());
cap.setCapability("applicationPath", getApplicationPath().getAbsoluteFile());
List supported = getSupportedDevices();
if (supported.contains(DeviceType.iphone)) {
cap.setDevice(DeviceType.iphone);
} else {
cap.setDevice(DeviceType.ipad);
}
if (this instanceof IPAApplication) {
cap.setCapability(IOSCapabilities.SIMULATOR, false);
} else {
cap.setCapability(IOSCapabilities.SIMULATOR, true);
}
cap.setCapability(IOSCapabilities.SUPPORTED_DEVICES, supported);
for (Iterator iterator = getMetadata().keys(); iterator.hasNext(); ) {
String key = (String) iterator.next();
try {
Object value = getMetadata().get(key);
cap.setCapability(key, value);
} catch (JSONException e) {
throw new WebDriverException("cannot get metadata", e);
}
}
return cap;
}
public static boolean canRun(IOSCapabilities desiredCapabilities, IOSCapabilities appCapability) {
if (desiredCapabilities.isSimulator() != null &&
desiredCapabilities.isSimulator() != appCapability.isSimulator()) {
return false;
}
if (desiredCapabilities.getBundleName() == null) {
throw new WebDriverException("you need to specify the bundle to test.");
}
String desired = desiredCapabilities.getBundleName();
String bundleName = (String) appCapability.getCapability(IOSCapabilities.BUNDLE_NAME);
String displayName = (String) appCapability.getCapability(IOSCapabilities.BUNDLE_DISPLAY_NAME);
String name = bundleName != null ? bundleName : displayName;
if (!desired.equals(name)) {
return false;
}
if (desiredCapabilities.getBundleVersion() != null && !desiredCapabilities.getBundleVersion()
.equals(appCapability.getBundleVersion())) {
return false;
}
if (desiredCapabilities.getDevice() == null) {
throw new WebDriverException("you need to specify the device.");
}
if (!(appCapability.getSupportedDevices()
.contains(desiredCapabilities.getDevice()))) {
return false;
}
// check any extra capability starting with plist_
for (String key : desiredCapabilities.getRawCapabilities().keySet()) {
if (key.startsWith(IOSCapabilities.MAGIC_PREFIX)) {
String realKey = key.replace(MAGIC_PREFIX, "");
if (!desiredCapabilities.getRawCapabilities().get(key)
.equals(appCapability.getRawCapabilities().get(realKey))) {
return false;
}
}
}
String l = desiredCapabilities.getLanguage();
if (appCapability.getSupportedLanguages().isEmpty()) {
log.info(
"The application doesn't have any content files."
+ " Localization related features won't be available.");
} else if (l != null && !appCapability.getSupportedLanguages().contains(l)) {
throw new SessionNotCreatedException(
"Language requested, " + l + " ,isn't supported.Supported are : "
+ appCapability.getSupportedLanguages());
}
return true;
}
public String getBundleVersion() {
return getMetadata(IOSCapabilities.BUNDLE_VERSION);
}
public String getApplicationName() {
String name = getMetadata(IOSCapabilities.BUNDLE_NAME);
String displayName = getMetadata(IOSCapabilities.BUNDLE_DISPLAY_NAME);
return (name != null) && ! name.trim().isEmpty() ? name : displayName;
}
public List getSupportedDevices() {
List families = new ArrayList();
String s = (String) getMetadata(IOSCapabilities.DEVICE_FAMILLY);
try {
JSONArray ar = new JSONArray(s);
for (int i = 0; i < ar.length(); i++) {
int f = ar.getInt(i);
if (f == 1) {
families.add(DeviceType.iphone);
families.add(DeviceType.ipod);
} else {
families.add(DeviceType.ipad);
}
}
return families;
} catch (JSONException e) {
throw new WebDriverException(e);
}
}
public boolean isSimulator() {
return "iphonesimulator".equals(getMetadata("DTPlatformName"));
}
public IOSRunningApplication createInstance(AppleLanguage language) {
return new IOSRunningApplication(language, this);
}
public static APPIOSApplication createFrom(File app) {
if (app == null || !app.exists()) {
return null;
} else if (app.getAbsolutePath().endsWith(".ipa")) {
return IPAApplication.createFrom(app);
} else if (app.getAbsolutePath().endsWith(".app")) {
return new APPIOSApplication(app.getAbsolutePath());
} else {
return null;
}
}
}