diff options
author | 2016-10-26 23:03:08 +0000 | |
---|---|---|
committer | 2016-10-26 23:03:08 +0000 | |
commit | 821de7335e490a4570d60b67ded5613e240c1777 (patch) | |
tree | dabb4aaec553b33278031c2ba6e9ae49c4f67c5c | |
parent | 5ced5eeb822d888baa49e63ba2148ef4ee55a9b5 (diff) | |
parent | a01e6667d93cbd12dd8ffbf00b5373aee0692388 (diff) |
Merge "wifi: hotspot2: omadm: add parsing support for PPS MO tree" am: ef0cc62058 am: bb8f3a3bbf am: e6effcd7a6
am: a01e6667d9
Change-Id: I555251fc739284af57179dfac48a4fb46c5a2a59
10 files changed, 1544 insertions, 0 deletions
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java new file mode 100644 index 000000000000..65a49ea6cecc --- /dev/null +++ b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java @@ -0,0 +1,786 @@ +/** + * Copyright (c) 2016, The Android Open Source Project + * + * 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 android.net.wifi.hotspot2.omadm; + +import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.pps.Credential; +import android.net.wifi.hotspot2.pps.HomeSP; +import android.text.TextUtils; +import android.util.Log; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.xml.sax.SAXException; + +/** + * Utility class for converting OMA-DM (Open Mobile Alliance's Device Management) + * PPS-MO (PerProviderSubscription Management Object) XML tree to a + * {@link PasspointConfiguration} object. + * + * Currently this only supports PerProviderSubscription/HomeSP and + * PerProviderSubscription/Credential subtree for Hotspot 2.0 Release 1 support. + * + * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 + * Release 2 Technical Specification. + * + * Below is a sample XML string for a Release 1 PPS MO tree: + * + * <MgmtTree xmlns="syncml:dmddf1.2"> + * <VerDTD>1.2</VerDTD> + * <Node> + * <NodeName>PerProviderSubscription</NodeName> + * <RTProperties> + * <Type> + * <DDFName>urn:wfa:mo:hotspot2dot0perprovidersubscription:1.0</DDFName> + * </Type> + * </RTProperties> + * <Node> + * <NodeName>i001</NodeName> + * <Node> + * <NodeName>HomeSP</NodeName> + * <Node> + * <NodeName>FriendlyName</NodeName> + * <Value>Century House</Value> + * </Node> + * <Node> + * <NodeName>FQDN</NodeName> + * <Value>mi6.co.uk</Value> + * </Node> + * <Node> + * <NodeName>RoamingConsortiumOI</NodeName> + * <Value>112233,445566</Value> + * </Node> + * </Node> + * <Node> + * <NodeName>Credential</NodeName> + * <Node> + * <NodeName>Realm</NodeName> + * <Value>shaken.stirred.com</Value> + * </Node> + * <Node> + * <NodeName>UsernamePassword</NodeName> + * <Node> + * <NodeName>Username</NodeName> + * <Value>james</Value> + * </Node> + * <Node> + * <NodeName>Password</NodeName> + * <Value>Ym9uZDAwNw==</Value> + * </Node> + * <Node> + * <NodeName>EAPMethod</NodeName> + * <Node> + * <NodeName>EAPType</NodeName> + * <Value>21</Value> + * </Node> + * <Node> + * <NodeName>InnerMethod</NodeName> + * <Value>MS-CHAP-V2</Value> + * </Node> + * </Node> + * </Node> + * </Node> + * </Node> + * </Node> + * </MgmtTree> + * + * @hide + */ +public final class PPSMOParser { + private static final String TAG = "PPSMOParser"; + + /** + * XML tags expected in the PPS MO (PerProviderSubscription Management Object) XML tree. + */ + private static final String TAG_MANAGEMENT_TREE = "MgmtTree"; + private static final String TAG_VER_DTD = "VerDTD"; + private static final String TAG_NODE = "Node"; + private static final String TAG_NODE_NAME = "NodeName"; + private static final String TAG_RT_PROPERTIES = "RTProperties"; + private static final String TAG_TYPE = "Type"; + private static final String TAG_DDF_NAME = "DDFName"; + private static final String TAG_VALUE = "Value"; + + /** + * Name for PerProviderSubscription node. + */ + private static final String NODE_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription"; + + /** + * Fields under HomeSP subtree. + */ + private static final String NODE_HOMESP = "HomeSP"; + private static final String NODE_FQDN = "FQDN"; + private static final String NODE_FRIENDLY_NAME = "FriendlyName"; + private static final String NODE_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI"; + + /** + * Fields under Credential subtree. + */ + private static final String NODE_CREDENTIAL = "Credential"; + private static final String NODE_USERNAME_PASSWORD = "UsernamePassword"; + private static final String NODE_USERNAME = "Username"; + private static final String NODE_PASSWORD = "Password"; + private static final String NODE_EAP_METHOD = "EAPMethod"; + private static final String NODE_EAP_TYPE = "EAPType"; + private static final String NODE_INNER_METHOD = "InnerMethod"; + private static final String NODE_DIGITAL_CERTIFICATE = "DigitalCertificate"; + private static final String NODE_CERTIFICATE_TYPE = "CertificateType"; + private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256FingerPrint"; + private static final String NODE_REALM = "Realm"; + private static final String NODE_SIM = "SIM"; + private static final String NODE_SIM_IMSI = "IMSI"; + + /** + * URN (Unique Resource Name) for PerProviderSubscription Management Object Tree. + */ + private static final String PPS_MO_URN = + "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"; + + /** + * Exception for generic parsing errors. + */ + private static class ParsingException extends Exception { + public ParsingException(String message) { + super(message); + } + } + + /** + * Class representing a node within the PerProviderSubscription tree. + * This is used to flatten out and eliminate the extra layering in the XMLNode tree, + * to make the data parsing easier and cleaner. + * + * A PPSNode can be an internal or a leaf node, but not both. + * + */ + private static abstract class PPSNode { + private final String mName; + public PPSNode(String name) { + mName = name; + } + + /** + * @return the name of the node + */ + public String getName() { + return mName; + } + + /** + * Applies for internal node only. + * + * @return the list of children nodes. + */ + public abstract List<PPSNode> getChildren(); + + /** + * Applies for leaf node only. + * + * @return the string value of the node + */ + public abstract String getValue(); + + /** + * @return a flag indicating if this is a leaf or an internal node + */ + public abstract boolean isLeaf(); + } + + /** + * Class representing a leaf node in a PPS (PerProviderSubscription) tree. + */ + private static class LeafNode extends PPSNode { + private final String mValue; + public LeafNode(String nodeName, String value) { + super(nodeName); + mValue = value; + } + + @Override + public String getValue() { + return mValue; + } + + @Override + public List<PPSNode> getChildren() { + return null; + } + + @Override + public boolean isLeaf() { + return true; + } + } + + /** + * Class representing an internal node in a PPS (PerProviderSubscription) tree. + */ + private static class InternalNode extends PPSNode { + private final List<PPSNode> mChildren; + public InternalNode(String nodeName, List<PPSNode> children) { + super(nodeName); + mChildren = children; + } + + @Override + public String getValue() { + return null; + } + + @Override + public List<PPSNode> getChildren() { + return mChildren; + } + + @Override + public boolean isLeaf() { + return false; + } + } + + /** + * Convert a XML string representation of a PPS MO (PerProviderSubscription + * Management Object) tree to a {@link PasspointConfiguration} object. + * + * @param xmlString XML string representation of a PPS MO tree + * @return {@link PasspointConfiguration} or null + */ + public static PasspointConfiguration parseMOText(String xmlString) { + // Convert the XML string to a XML tree. + XMLParser xmlParser = new XMLParser(); + XMLNode root = null; + try { + root = xmlParser.parse(xmlString); + } catch(IOException | SAXException e) { + return null; + } + if (root == null) { + return null; + } + + // Verify root node is a "MgmtTree" node. + if (root.getTag() != TAG_MANAGEMENT_TREE) { + Log.e(TAG, "Root is not a MgmtTree"); + return null; + } + + String verDtd = null; // Used for detecting duplicate VerDTD element. + PasspointConfiguration config = null; + for (XMLNode child : root.getChildren()) { + switch(child.getTag()) { + case TAG_VER_DTD: + if (verDtd != null) { + Log.e(TAG, "Duplicate VerDTD element"); + return null; + } + verDtd = child.getText(); + break; + case TAG_NODE: + if (config != null) { + Log.e(TAG, "Unexpected multiple Node element under MgmtTree"); + return null; + } + try { + config = parsePpsNode(child); + } catch (ParsingException e) { + Log.e(TAG, e.getMessage()); + return null; + } + break; + default: + Log.e(TAG, "Unknown node: " + child.getTag()); + return null; + } + } + return config; + } + + /** + * Parse a PerProviderSubscription node. Below is the format of the XML tree (with + * each XML element represent a node in the tree): + * + * <Node> + * <NodeName>PerProviderSubscription</NodeName> + * <RTProperties> + * ... + * </RTPProperties> + * <Node> + * ... + * </Node> + * </Node> + * + * @param node XMLNode that contains PerProviderSubscription node. + * @return PasspointConfiguration or null + * @throws ParsingException + */ + private static PasspointConfiguration parsePpsNode(XMLNode node) + throws ParsingException { + PasspointConfiguration config = null; + String nodeName = null; + for (XMLNode child : node.getChildren()) { + switch (child.getTag()) { + case TAG_NODE_NAME: + if (nodeName != null) { + throw new ParsingException("Duplicant NodeName: " + child.getText()); + } + nodeName = child.getText(); + if (!TextUtils.equals(nodeName, NODE_PER_PROVIDER_SUBSCRIPTION)) { + throw new ParsingException("Unexpected NodeName: " + nodeName); + } + break; + case TAG_NODE: + // Only one PerProviderSubscription instance is expected and allowed. + if (config != null) { + throw new ParsingException("Multiple PPS instance"); + } + // Convert the XML tree to a PPS tree. + PPSNode ppsInstanceRoot = buildPpsNode(child); + config = parsePpsInstance(ppsInstanceRoot); + break; + case TAG_RT_PROPERTIES: + // Parse and verify URN stored in the RT (Run Time) Properties. + String urn = parseUrn(child); + if (!TextUtils.equals(urn, PPS_MO_URN)) { + throw new ParsingException("Unknown URN: " + urn); + } + break; + default: + throw new ParsingException("Unknown tag under PPS node: " + child.getTag()); + } + } + return config; + } + + /** + * Parse the URN stored in the RTProperties. Below is the format of the RTPProperties node: + * + * <RTProperties> + * <Type> + * <DDFName>urn:...</DDFName> + * </Type> + * </RTProperties> + * + * @param node XMLNode that contains RTProperties node. + * @return URN String of URN. + * @throws ParsingException + */ + private static String parseUrn(XMLNode node) throws ParsingException { + if (node.getChildren().size() != 1) + throw new ParsingException("Expect RTPProperties node to only have one child"); + + XMLNode typeNode = node.getChildren().get(0); + if (typeNode.getChildren().size() != 1) { + throw new ParsingException("Expect Type node to only have one child"); + } + if (!TextUtils.equals(typeNode.getTag(), TAG_TYPE)) { + throw new ParsingException("Unexpected tag for Type: " + typeNode.getTag()); + } + + XMLNode ddfNameNode = typeNode.getChildren().get(0); + if (!ddfNameNode.getChildren().isEmpty()) { + throw new ParsingException("Expect DDFName node to have no child"); + } + if (!TextUtils.equals(ddfNameNode.getTag(), TAG_DDF_NAME)) { + throw new ParsingException("Unexpected tag for DDFName: " + ddfNameNode.getTag()); + } + + return ddfNameNode.getText(); + } + + /** + * Convert a XML tree represented by XMLNode to a PPS (PerProviderSubscription) instance tree + * represented by PPSNode. This flattens out the XML tree to allow easier and cleaner parsing + * of the PPS configuration data. Only three types of XML tag are expected: "NodeName", + * "Node", and "Value". + * + * The original XML tree (each XML element represent a node): + * + * <Node> + * <NodeName>root</NodeName> + * <Node> + * <NodeName>child1</NodeName> + * <Value>value1</Value> + * </Node> + * <Node> + * <NodeName>child2</NodeName> + * <Node> + * <NodeName>grandchild1</NodeName> + * ... + * </Node> + * </Node> + * ... + * </Node> + * + * The converted PPS tree: + * + * [root] --- [child1, value1] + * | + * ---------[child2] --------[grandchild1] --- ... + * + * @param node XMLNode pointed to the root of a XML tree + * @return PPSNode pointing to the root of a PPS tree + * @throws ParsingException + */ + private static PPSNode buildPpsNode(XMLNode node) throws ParsingException { + String nodeName = null; + String nodeValue = null; + List<PPSNode> childNodes = new ArrayList<PPSNode>(); + // Names of parsed child nodes, use for detecting multiple child nodes with the same name. + Set<String> parsedNodes = new HashSet<String>(); + + for (XMLNode child : node.getChildren()) { + String tag = child.getTag(); + if (TextUtils.equals(tag, TAG_NODE_NAME)) { + if (nodeName != null) { + throw new ParsingException("Duplicate NodeName node"); + } + nodeName = child.getText(); + } else if (TextUtils.equals(tag, TAG_NODE)) { + PPSNode ppsNode = buildPpsNode(child); + if (parsedNodes.contains(ppsNode.getName())) { + throw new ParsingException("Duplicate node: " + ppsNode.getName()); + } + parsedNodes.add(ppsNode.getName()); + childNodes.add(ppsNode); + } else if (TextUtils.equals(tag, TAG_VALUE)) { + if (nodeValue != null) { + throw new ParsingException("Duplicate Value node"); + } + nodeValue = child.getText(); + } else { + throw new ParsingException("Unknown tag: " + tag); + } + } + + if (nodeName == null) { + throw new ParsingException("Invalid node: missing NodeName"); + } + if (nodeValue == null && childNodes.size() == 0) { + throw new ParsingException("Invalid node: " + nodeName + + " missing both value and children"); + } + if (nodeValue != null && childNodes.size() > 0) { + throw new ParsingException("Invalid node: " + nodeName + + " contained both value and children"); + } + + if (nodeValue != null) { + return new LeafNode(nodeName, nodeValue); + } + return new InternalNode(nodeName, childNodes); + } + + /** + * Return the value of a PPSNode. An exception will be thrown if the given node + * is not a leaf node. + * + * @param node PPSNode to retrieve the value from + * @return String representing the value of the node + * @throws ParsingException + */ + private static String getPpsNodeValue(PPSNode node) throws ParsingException { + if (!node.isLeaf()) { + throw new ParsingException("Cannot get value from a non-leaf node: " + node.getName()); + } + return node.getValue(); + } + + /** + * Parse a PPS (PerProviderSubscription) configurations from a PPS tree. + * + * @param root PPSNode representing the root of the PPS tree + * @return PasspointConfiguration + * @throws ParsingException + */ + private static PasspointConfiguration parsePpsInstance(PPSNode root) + throws ParsingException { + if (root.isLeaf()) { + throw new ParsingException("Leaf node not expected for PPS instance"); + } + + PasspointConfiguration config = new PasspointConfiguration(); + for (PPSNode child : root.getChildren()) { + switch(child.getName()) { + case NODE_HOMESP: + config.homeSp = parseHomeSP(child); + break; + case NODE_CREDENTIAL: + config.credential = parseCredential(child); + break; + default: + throw new ParsingException("Unknown node: " + child.getName()); + } + } + return config; + } + + /** + * Parse configurations under PerProviderSubscription/HomeSP subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP subtree + * @return HomeSP + * @throws ParsingException + */ + private static HomeSP parseHomeSP(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for HomeSP"); + } + + HomeSP homeSp = new HomeSP(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_FQDN: + homeSp.fqdn = getPpsNodeValue(child); + break; + case NODE_FRIENDLY_NAME: + homeSp.friendlyName = getPpsNodeValue(child); + break; + case NODE_ROAMING_CONSORTIUM_OI: + homeSp.roamingConsortiumOIs = + parseRoamingConsortiumOI(getPpsNodeValue(child)); + break; + default: + throw new ParsingException("Unknown node under HomeSP: " + child.getName()); + } + } + return homeSp; + } + + /** + * Parse the roaming consortium OI string, which contains a list of OIs separated by ",". + * + * @param oiStr string containing list of OIs (Organization Identifiers) separated by "," + * @return long[] + * @throws ParsingException + */ + private static long[] parseRoamingConsortiumOI(String oiStr) + throws ParsingException { + String[] oiStrArray = oiStr.split(","); + long[] oiArray = new long[oiStrArray.length]; + for (int i = 0; i < oiStrArray.length; i++) { + try { + oiArray[i] = Long.parseLong(oiStrArray[i], 16); + } catch (NumberFormatException e) { + throw new ParsingException("Invalid OI: " + oiStrArray[i]); + } + } + return oiArray; + } + + /** + * Parse configurations under PerProviderSubscription/Credential subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/Credential subtree + * @return Credential + * @throws ParsingException + */ + private static Credential parseCredential(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for HomeSP"); + } + + Credential credential = new Credential(); + for (PPSNode child: node.getChildren()) { + switch (child.getName()) { + case NODE_USERNAME_PASSWORD: + credential.userCredential = parseUserCredential(child); + break; + case NODE_DIGITAL_CERTIFICATE: + credential.certCredential = parseCertificateCredential(child); + break; + case NODE_REALM: + credential.realm = getPpsNodeValue(child); + break; + case NODE_SIM: + credential.simCredential = parseSimCredential(child); + break; + default: + throw new ParsingException("Unknown node under Credential: " + + child.getName()); + } + } + return credential; + } + + /** + * Parse configurations under PerProviderSubscription/Credential/UsernamePassword subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Credential/UsernamePassword subtree + * @return Credential.UserCredential + * @throws ParsingException + */ + private static Credential.UserCredential parseUserCredential(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for UsernamePassword"); + } + + Credential.UserCredential userCred = new Credential.UserCredential(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_USERNAME: + userCred.username = getPpsNodeValue(child); + break; + case NODE_PASSWORD: + userCred.password = getPpsNodeValue(child); + break; + case NODE_EAP_METHOD: + parseEAPMethod(child, userCred); + break; + default: + throw new ParsingException("Unknown node under UsernamPassword: " + + child.getName()); + } + } + return userCred; + } + + /** + * Parse configurations under PerProviderSubscription/Credential/UsernamePassword/EAPMethod + * subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Credential/UsernamePassword/EAPMethod subtree + * @param userCred UserCredential to be updated with EAP method values. + * @throws ParsingException + */ + private static void parseEAPMethod(PPSNode node, Credential.UserCredential userCred) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for EAPMethod"); + } + + for (PPSNode child : node.getChildren()) { + switch(child.getName()) { + case NODE_EAP_TYPE: + userCred.eapType = parseInteger(getPpsNodeValue(child)); + break; + case NODE_INNER_METHOD: + userCred.nonEapInnerMethod = getPpsNodeValue(child); + break; + default: + throw new ParsingException("Unknown node under EAPMethod: " + child.getName()); + } + } + } + + /** + * Parse configurations under PerProviderSubscription/Credential/DigitalCertificate subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Credential/DigitalCertificate subtree + * @return Credential.CertificateCredential + * @throws ParsingException + */ + private static Credential.CertificateCredential parseCertificateCredential(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for DigitalCertificate"); + } + + Credential.CertificateCredential certCred = new Credential.CertificateCredential(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_CERTIFICATE_TYPE: + certCred.certType = getPpsNodeValue(child); + break; + case NODE_CERT_SHA256_FINGERPRINT: + certCred.certSha256FingerPrint = parseHexString(getPpsNodeValue(child)); + break; + default: + throw new ParsingException("Unknown node under DigitalCertificate: " + + child.getName()); + } + } + return certCred; + } + + /** + * Parse configurations under PerProviderSubscription/Credential/SIM subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/Credential/SIM + * subtree + * @return Credential.SimCredential + * @throws ParsingException + */ + private static Credential.SimCredential parseSimCredential(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for SIM"); + } + + Credential.SimCredential simCred = new Credential.SimCredential(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_SIM_IMSI: + simCred.imsi = getPpsNodeValue(child); + break; + case NODE_EAP_TYPE: + simCred.eapType = parseInteger(getPpsNodeValue(child)); + break; + default: + throw new ParsingException("Unknown node under SIM: " + child.getName()); + } + } + return simCred; + } + + /** + * Convert a hex string to a byte array. + * + * @param str String containing hex values + * @return byte[] + * @throws ParsingException + */ + private static byte[] parseHexString(String str) throws ParsingException { + if ((str.length() & 1) == 1) { + throw new ParsingException("Odd length hex string: " + str.length()); + } + + byte[] result = new byte[str.length() / 2]; + for (int i = 0; i < result.length; i++) { + int index = i * 2; + try { + result[i] = (byte) Integer.parseInt(str.substring(index, index + 2), 16); + } catch (NumberFormatException e) { + throw new ParsingException("Invalid hex string: " + str); + } + } + return result; + } + + /** + * Parse an integer string. + * + * @param value String of integer value + * @return int + * @throws ParsingException + */ + private static int parseInteger(String value) throws ParsingException { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new ParsingException("Invalid integer value: " + value); + } + } +} diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml new file mode 100644 index 000000000000..53d38ad23b9b --- /dev/null +++ b/wifi/tests/assets/pps/PerProviderSubscription.xml @@ -0,0 +1,80 @@ +<MgmtTree xmlns="syncml:dmddf1.2"> + <VerDTD>1.2</VerDTD> + <Node> + <NodeName>PerProviderSubscription</NodeName> + <RTProperties> + <Type> + <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName> + </Type> + </RTProperties> + <Node> + <NodeName>i001</NodeName> + <Node> + <NodeName>HomeSP</NodeName> + <Node> + <NodeName>FriendlyName</NodeName> + <Value>Century House</Value> + </Node> + <Node> + <NodeName>FQDN</NodeName> + <Value>mi6.co.uk</Value> + </Node> + <Node> + <NodeName>RoamingConsortiumOI</NodeName> + <Value>112233,445566</Value> + </Node> + </Node> + <Node> + <NodeName>Credential</NodeName> + <Node> + <NodeName>Realm</NodeName> + <Value>shaken.stirred.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>james</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>Ym9uZDAwNw==</Value> + </Node> + <Node> + <NodeName>EAPMethod</NodeName> + <Node> + <NodeName>EAPType</NodeName> + <Value>21</Value> + </Node> + <Node> + <NodeName>InnerMethod</NodeName> + <Value>MS-CHAP-V2</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>DigitalCertificate</NodeName> + <Node> + <NodeName>CertificateType</NodeName> + <Value>x509v3</Value> + </Node> + <Node> + <NodeName>CertSHA256FingerPrint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + <Node> + <NodeName>SIM</NodeName> + <Node> + <NodeName>IMSI</NodeName> + <Value>imsi</Value> + </Node> + <Node> + <NodeName>EAPType</NodeName> + <Value>24</Value> + </Node> + </Node> + </Node> + </Node> + </Node> +</MgmtTree> diff --git a/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml new file mode 100644 index 000000000000..e13eb2a2d85e --- /dev/null +++ b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateHomeSP.xml @@ -0,0 +1,95 @@ +<MgmtTree xmlns="syncml:dmddf1.2"> + <VerDTD>1.2</VerDTD> + <Node> + <NodeName>PerProviderSubscription</NodeName> + <RTProperties> + <Type> + <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName> + </Type> + </RTProperties> + <Node> + <NodeName>i001</NodeName> + <Node> + <NodeName>HomeSP</NodeName> + <Node> + <NodeName>FriendlyName</NodeName> + <Value>Century House</Value> + </Node> + <Node> + <NodeName>FQDN</NodeName> + <Value>mi6.co.uk</Value> + </Node> + <Node> + <NodeName>RoamingConsortiumOI</NodeName> + <Value>112233,445566</Value> + </Node> + </Node> + <Node> + <NodeName>HomeSP</NodeName> + <Node> + <NodeName>FriendlyName</NodeName> + <Value>Century House</Value> + </Node> + <Node> + <NodeName>FQDN</NodeName> + <Value>mi6.co.uk</Value> + </Node> + <Node> + <NodeName>RoamingConsortiumOI</NodeName> + <Value>112233,445566</Value> + </Node> + </Node> + <Node> + <NodeName>Credential</NodeName> + <Node> + <NodeName>Realm</NodeName> + <Value>shaken.stirred.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>james</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>Ym9uZDAwNw==</Value> + </Node> + <Node> + <NodeName>EAPMethod</NodeName> + <Node> + <NodeName>EAPType</NodeName> + <Value>21</Value> + </Node> + <Node> + <NodeName>InnerMethod</NodeName> + <Value>MS-CHAP-V2</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>DigitalCertificate</NodeName> + <Node> + <NodeName>CertificateType</NodeName> + <Value>x509v3</Value> + </Node> + <Node> + <NodeName>CertSHA256FingerPrint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + <Node> + <NodeName>SIM</NodeName> + <Node> + <NodeName>IMSI</NodeName> + <Value>imsi</Value> + </Node> + <Node> + <NodeName>EAPType</NodeName> + <Value>24</Value> + </Node> + </Node> + </Node> + </Node> + </Node> +</MgmtTree> diff --git a/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml new file mode 100644 index 000000000000..8719ffaaa6e6 --- /dev/null +++ b/wifi/tests/assets/pps/PerProviderSubscription_DuplicateValue.xml @@ -0,0 +1,81 @@ +<MgmtTree xmlns="syncml:dmddf1.2"> + <VerDTD>1.2</VerDTD> + <Node> + <NodeName>PerProviderSubscription</NodeName> + <RTProperties> + <Type> + <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName> + </Type> + </RTProperties> + <Node> + <NodeName>i001</NodeName> + <Node> + <NodeName>HomeSP</NodeName> + <Node> + <NodeName>FriendlyName</NodeName> + <Value>Century House</Value> + <Value>Century House</Value> + </Node> + <Node> + <NodeName>FQDN</NodeName> + <Value>mi6.co.uk</Value> + </Node> + <Node> + <NodeName>RoamingConsortiumOI</NodeName> + <Value>112233,445566</Value> + </Node> + </Node> + <Node> + <NodeName>Credential</NodeName> + <Node> + <NodeName>Realm</NodeName> + <Value>shaken.stirred.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>james</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>Ym9uZDAwNw==</Value> + </Node> + <Node> + <NodeName>EAPMethod</NodeName> + <Node> + <NodeName>EAPType</NodeName> + <Value>21</Value> + </Node> + <Node> + <NodeName>InnerMethod</NodeName> + <Value>MS-CHAP-V2</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>DigitalCertificate</NodeName> + <Node> + <NodeName>CertificateType</NodeName> + <Value>x509v3</Value> + </Node> + <Node> + <NodeName>CertSHA256FingerPrint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + <Node> + <NodeName>SIM</NodeName> + <Node> + <NodeName>IMSI</NodeName> + <Value>imsi</Value> + </Node> + <Node> + <NodeName>EAPType</NodeName> + <Value>24</Value> + </Node> + </Node> + </Node> + </Node> + </Node> +</MgmtTree> diff --git a/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml b/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml new file mode 100644 index 000000000000..c761237b2f18 --- /dev/null +++ b/wifi/tests/assets/pps/PerProviderSubscription_InvalidName.xml @@ -0,0 +1,80 @@ +<MgmtTree xmlns="syncml:dmddf1.2"> + <VerDTD>1.2</VerDTD> + <Node> + <NodeName>PerProviderSubscription</NodeName> + <RTProperties> + <Type> + <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName> + </Type> + </RTProperties> + <Node> + <NodeName>i001</NodeName> + <Node> + <NodeName>HomeSP</NodeName> + <Node> + <NodeName>FriendName</NodeName> + <Value>Century House</Value> + </Node> + <Node> + <NodeName>FQDN</NodeName> + <Value>mi6.co.uk</Value> + </Node> + <Node> + <NodeName>RoamingConsortiumOI</NodeName> + <Value>112233,445566</Value> + </Node> + </Node> + <Node> + <NodeName>Credential</NodeName> + <Node> + <NodeName>Realm</NodeName> + <Value>shaken.stirred.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>james</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>Ym9uZDAwNw==</Value> + </Node> + <Node> + <NodeName>EAPMethod</NodeName> + <Node> + <NodeName>EAPType</NodeName> + <Value>21</Value> + </Node> + <Node> + <NodeName>InnerMethod</NodeName> + <Value>MS-CHAP-V2</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>DigitalCertificate</NodeName> + <Node> + <NodeName>CertificateType</NodeName> + <Value>x509v3</Value> + </Node> + <Node> + <NodeName>CertSHA256FingerPrint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + <Node> + <NodeName>SIM</NodeName> + <Node> + <NodeName>IMSI</NodeName> + <Value>imsi</Value> + </Node> + <Node> + <NodeName>EAPType</NodeName> + <Value>24</Value> + </Node> + </Node> + </Node> + </Node> + </Node> +</MgmtTree> diff --git a/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml b/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml new file mode 100644 index 000000000000..6b807af2e774 --- /dev/null +++ b/wifi/tests/assets/pps/PerProviderSubscription_InvalidNode.xml @@ -0,0 +1,84 @@ +<MgmtTree xmlns="syncml:dmddf1.2"> + <VerDTD>1.2</VerDTD> + <Node> + <NodeName>PerProviderSubscription</NodeName> + <RTProperties> + <Type> + <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName> + </Type> + </RTProperties> + <Node> + <NodeName>i001</NodeName> + <Node> + <NodeName>HomeSP</NodeName> + <Node> + <NodeName>FriendlyName</NodeName> + <Value>Century House</Value> + </Node> + <Node> + <NodeName>FQDN</NodeName> + <Value>mi6.co.uk</Value> + <Node> + <NodeName>InvalidNode</NodeName> + <Value>Test</Value> + </Node> + </Node> + <Node> + <NodeName>RoamingConsortiumOI</NodeName> + <Value>112233,445566</Value> + </Node> + </Node> + <Node> + <NodeName>Credential</NodeName> + <Node> + <NodeName>Realm</NodeName> + <Value>shaken.stirred.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>james</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>Ym9uZDAwNw==</Value> + </Node> + <Node> + <NodeName>EAPMethod</NodeName> + <Node> + <NodeName>EAPType</NodeName> + <Value>21</Value> + </Node> + <Node> + <NodeName>InnerMethod</NodeName> + <Value>MS-CHAP-V2</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>DigitalCertificate</NodeName> + <Node> + <NodeName>CertificateType</NodeName> + <Value>x509v3</Value> + </Node> + <Node> + <NodeName>CertSHA256FingerPrint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + <Node> + <NodeName>SIM</NodeName> + <Node> + <NodeName>IMSI</NodeName> + <Value>imsi</Value> + </Node> + <Node> + <NodeName>EAPType</NodeName> + <Value>24</Value> + </Node> + </Node> + </Node> + </Node> + </Node> +</MgmtTree> diff --git a/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml b/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml new file mode 100644 index 000000000000..ed06b47a0c4d --- /dev/null +++ b/wifi/tests/assets/pps/PerProviderSubscription_MissingName.xml @@ -0,0 +1,79 @@ +<MgmtTree xmlns="syncml:dmddf1.2"> + <VerDTD>1.2</VerDTD> + <Node> + <NodeName>PerProviderSubscription</NodeName> + <RTProperties> + <Type> + <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName> + </Type> + </RTProperties> + <Node> + <NodeName>i001</NodeName> + <Node> + <Node> + <NodeName>FriendlyName</NodeName> + <Value>Century House</Value> + </Node> + <Node> + <NodeName>FQDN</NodeName> + <Value>mi6.co.uk</Value> + </Node> + <Node> + <NodeName>RoamingConsortiumOI</NodeName> + <Value>112233,445566</Value> + </Node> + </Node> + <Node> + <NodeName>Credential</NodeName> + <Node> + <NodeName>Realm</NodeName> + <Value>shaken.stirred.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>james</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>Ym9uZDAwNw==</Value> + </Node> + <Node> + <NodeName>EAPMethod</NodeName> + <Node> + <NodeName>EAPType</NodeName> + <Value>21</Value> + </Node> + <Node> + <NodeName>InnerMethod</NodeName> + <Value>MS-CHAP-V2</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>DigitalCertificate</NodeName> + <Node> + <NodeName>CertificateType</NodeName> + <Value>x509v3</Value> + </Node> + <Node> + <NodeName>CertSHA256FingerPrint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + <Node> + <NodeName>SIM</NodeName> + <Node> + <NodeName>IMSI</NodeName> + <Value>imsi</Value> + </Node> + <Node> + <NodeName>EAPType</NodeName> + <Value>24</Value> + </Node> + </Node> + </Node> + </Node> + </Node> +</MgmtTree> diff --git a/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml b/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml new file mode 100644 index 000000000000..f7e35ddee5a4 --- /dev/null +++ b/wifi/tests/assets/pps/PerProviderSubscription_MissingValue.xml @@ -0,0 +1,79 @@ +<MgmtTree xmlns="syncml:dmddf1.2"> + <VerDTD>1.2</VerDTD> + <Node> + <NodeName>PerProviderSubscription</NodeName> + <RTProperties> + <Type> + <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName> + </Type> + </RTProperties> + <Node> + <NodeName>i001</NodeName> + <Node> + <NodeName>HomeSP</NodeName> + <Node> + <NodeName>FriendlyName</NodeName> + </Node> + <Node> + <NodeName>FQDN</NodeName> + <Value>mi6.co.uk</Value> + </Node> + <Node> + <NodeName>RoamingConsortiumOI</NodeName> + <Value>112233,445566</Value> + </Node> + </Node> + <Node> + <NodeName>Credential</NodeName> + <Node> + <NodeName>Realm</NodeName> + <Value>shaken.stirred.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>james</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>Ym9uZDAwNw==</Value> + </Node> + <Node> + <NodeName>EAPMethod</NodeName> + <Node> + <NodeName>EAPType</NodeName> + <Value>21</Value> + </Node> + <Node> + <NodeName>InnerMethod</NodeName> + <Value>MS-CHAP-V2</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>DigitalCertificate</NodeName> + <Node> + <NodeName>CertificateType</NodeName> + <Value>x509v3</Value> + </Node> + <Node> + <NodeName>CertSHA256FingerPrint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + <Node> + <NodeName>SIM</NodeName> + <Node> + <NodeName>IMSI</NodeName> + <Value>imsi</Value> + </Node> + <Node> + <NodeName>EAPType</NodeName> + <Value>24</Value> + </Node> + </Node> + </Node> + </Node> + </Node> +</MgmtTree> diff --git a/wifi/tests/assets/pps/README.txt b/wifi/tests/assets/pps/README.txt new file mode 100644 index 000000000000..369c0a943d1b --- /dev/null +++ b/wifi/tests/assets/pps/README.txt @@ -0,0 +1,7 @@ +PerProviderSubscription.xml - valid PPS XML file +PerProviderSubscription_DuplicateHomeSP.xml - containing multiple HomeSP node +PerProviderSubscription_DuplicateValue.xml - FriendlyName node contains multiple Value +PerProviderSubscription_MissingValue.xml - FriendlyName node is missing Value +PerProviderSubscription_MissingName.xml - HomeSP node is missing NodeName +PerProviderSubscription_InvalidNode.xml - FQDN node contains both Value and a child node +PerProviderSubscription_InvalidName.xml - FriendlyName node have a typo in its name diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java new file mode 100644 index 000000000000..10b02677a15b --- /dev/null +++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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 android.net.wifi.hotspot2.omadm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.net.wifi.hotspot2.omadm.PPSMOParser; +import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.pps.Credential; +import android.net.wifi.hotspot2.pps.HomeSP; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; + +/** + * Unit tests for {@link android.net.wifi.hotspot2.omadm.PPSMOParser}. + */ +@SmallTest +public class PPSMOParserTest { + private static final String VALID_PPS_MO_XML_FILE = "assets/pps/PerProviderSubscription.xml"; + private static final String PPS_MO_XML_FILE_DUPLICATE_HOMESP = + "assets/pps/PerProviderSubscription_DuplicateHomeSP.xml"; + private static final String PPS_MO_XML_FILE_DUPLICATE_VALUE = + "assets/pps/PerProviderSubscription_DuplicateValue.xml"; + private static final String PPS_MO_XML_FILE_MISSING_VALUE = + "assets/pps/PerProviderSubscription_MissingValue.xml"; + private static final String PPS_MO_XML_FILE_MISSING_NAME = + "assets/pps/PerProviderSubscription_MissingName.xml"; + private static final String PPS_MO_XML_FILE_INVALID_NODE = + "assets/pps/PerProviderSubscription_InvalidNode.xml"; + private static final String PPS_MO_XML_FILE_INVALID_NAME = + "assets/pps/PerProviderSubscription_InvalidName.xml"; + + /** + * Read the content of the given resource file into a String. + * + * @param filename String name of the file + * @return String + * @throws IOException + */ + private String loadResourceFile(String filename) throws IOException { + InputStream in = getClass().getClassLoader().getResourceAsStream(filename); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line).append("\n"); + } + + return builder.toString(); + } + + /** + * Generate a {@link PasspointConfiguration} that matches the configuration specified in the + * XML file {@link #VALID_PPS_MO_XML_FILE}. + * + * @return {@link PasspointConfiguration} + */ + private PasspointConfiguration generateConfigurationFromPPSMOTree() { + PasspointConfiguration config = new PasspointConfiguration(); + + // HomeSP configuration. + config.homeSp = new HomeSP(); + config.homeSp.friendlyName = "Century House"; + config.homeSp.fqdn = "mi6.co.uk"; + config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L}; + + // Credential configuration. + config.credential = new Credential(); + config.credential.realm = "shaken.stirred.com"; + config.credential.userCredential = new Credential.UserCredential(); + config.credential.userCredential.username = "james"; + config.credential.userCredential.password = "Ym9uZDAwNw=="; + config.credential.userCredential.eapType = 21; + config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2"; + config.credential.certCredential = new Credential.CertificateCredential(); + config.credential.certCredential.certType = "x509v3"; + config.credential.certCredential.certSha256FingerPrint = new byte[32]; + Arrays.fill(config.credential.certCredential.certSha256FingerPrint, (byte)0x1f); + config.credential.simCredential = new Credential.SimCredential(); + config.credential.simCredential.imsi = "imsi"; + config.credential.simCredential.eapType = 24; + return config; + } + + /** + * Parse and verify all supported fields under PPS MO tree (currently only fields under + * HomeSP and Credential subtree). + * + * @throws Exception + */ + @Test + public void parseValidPPSMOTree() throws Exception { + String ppsMoTree = loadResourceFile(VALID_PPS_MO_XML_FILE); + PasspointConfiguration expectedConfig = generateConfigurationFromPPSMOTree(); + PasspointConfiguration actualConfig = PPSMOParser.parseMOText(ppsMoTree); + assertTrue(actualConfig.equals(expectedConfig)); + } + + @Test + public void parseNullPPSMOTree() throws Exception { + assertEquals(null, PPSMOParser.parseMOText(null)); + } + + @Test + public void parseEmptyPPSMOTree() throws Exception { + assertEquals(null, PPSMOParser.parseMOText(new String())); + } + + @Test + public void parsePPSMOTreeWithDuplicateHomeSP() throws Exception { + assertEquals(null, PPSMOParser.parseMOText( + loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_HOMESP))); + } + + @Test + public void parsePPSMOTreeWithDuplicateValue() throws Exception { + assertEquals(null, PPSMOParser.parseMOText( + loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_VALUE))); + } + + @Test + public void parsePPSMOTreeWithMissingValue() throws Exception { + assertEquals(null, PPSMOParser.parseMOText( + loadResourceFile(PPS_MO_XML_FILE_MISSING_VALUE))); + } + + @Test + public void parsePPSMOTreeWithMissingName() throws Exception { + assertEquals(null, PPSMOParser.parseMOText( + loadResourceFile(PPS_MO_XML_FILE_MISSING_NAME))); + } + + @Test + public void parsePPSMOTreeWithInvalidNode() throws Exception { + assertEquals(null, PPSMOParser.parseMOText( + loadResourceFile(PPS_MO_XML_FILE_INVALID_NODE))); + } + + @Test + public void parsePPSMOTreeWithInvalidName() throws Exception { + assertEquals(null, PPSMOParser.parseMOText( + loadResourceFile(PPS_MO_XML_FILE_INVALID_NAME))); + } +} + + + + + + + |