diff options
author | Peter Qiu <zqiu@google.com> | 2016-12-05 16:11:37 -0800 |
---|---|---|
committer | Peter Qiu <zqiu@google.com> | 2016-12-15 15:37:19 -0800 |
commit | 74339de52d7066f22771d914e698da503232c107 (patch) | |
tree | f38b4466b773e40a6ddfb6eec9c2bdc2084542e9 /tests | |
parent | fa04a81daf829e6e5c099c9a249b8dd8dd112102 (diff) |
hotspot2: ANQP elements cleanup Part 1
Cleanup and add unit tests for the following ANQP elements (and
the underlying classes used by those elements):
- HSFriendNameElement
- IPAddressTypeAvailabilityElement
- RoamingConsortiumElement
- VenueNameElement
The cleanup included using a static #parse function for parsing
raw bytes into an element object, the new ByteBufferReader APIs
for reading integer and string from ByteBuffer, and documented
possible runtime exceptions.
Additional changes include:
- remove the unnecessary setting of byte order for
the ByteBuffer, since we're not using the ByteBuffer's APIs
for reading integer values (all reads are either byte or byte
array).
- remove the unused functions in ANQPFactory
More ANQP elements cleanup will be done in the upcoming CLs.
Bug: 33000864
Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh
Change-Id: I6da918c83722d5c0ca7a2374ff5fa5f630cdea6d
Diffstat (limited to 'tests')
5 files changed, 668 insertions, 164 deletions
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java new file mode 100644 index 000000000..e228e0391 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java @@ -0,0 +1,170 @@ +/* + * 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 com.android.server.wifi.hotspot2.anqp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.ProtocolException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSFriendlyNameElement}. + */ +@SmallTest +public class HSFriendlyNameElementTest { + private static final String TEST_LANGUAGE = "en"; + private static final Locale TEST_LOCALE = Locale.forLanguageTag(TEST_LANGUAGE); + private static final String TEST_OPERATOR_NAME1 = "Operator1"; + private static final String TEST_OPERATOR_NAME2 = "Operator2"; + + /** + * Helper function for appending a Operator Name to an output stream. + * + * @param stream Stream to write to + * @param operator The name of the operator + * @throws IOException + */ + private void appendOperatorName(ByteArrayOutputStream stream, String operator) + throws IOException { + byte[] nameBytes = operator.getBytes(StandardCharsets.UTF_8); + int length = I18Name.LANGUAGE_CODE_LENGTH + operator.length(); + stream.write((byte) length); + stream.write(TEST_LANGUAGE.getBytes(StandardCharsets.US_ASCII)); + stream.write(new byte[]{(byte) 0x0}); // Padding for language code. + stream.write(nameBytes); + } + + /** + * Helper function for generating test data. + * + * @return byte[] of data + * @throws IOException + */ + private byte[] getTestData(String[] names) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + for (String name : names) { + appendOperatorName(stream, name); + } + return stream.toByteArray(); + } + + /** + * Verify that HSFriendlyNameElement with a empty operator name list will be returned when + * parsing an empty buffer. + * + * @throws Exception + */ + @Test + public void parseBufferWithEmptyBuffer() throws Exception { + assertTrue(HSFriendlyNameElement.parse(ByteBuffer.allocate(0)).getNames().isEmpty()); + } + + /** + * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer + * (missing a byte at the end). + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseBufferWithTruncatedByte() throws Exception { + byte[] testData = getTestData(new String[] {TEST_OPERATOR_NAME1}); + // Truncate a byte at the end. + ByteBuffer buffer = ByteBuffer.allocate(testData.length - 1); + buffer.put(testData, 0, testData.length - 1); + buffer.position(0); + HSFriendlyNameElement.parse(buffer); + } + + /** + * Verify that an expected HSFriendlyNameElement will be returned when parsing a buffer + * containing the default test data. + * + * @throws Exception + */ + @Test + public void parseBufferWithDefaultTestData() throws Exception { + byte[] testData = getTestData(new String[] {TEST_OPERATOR_NAME1, TEST_OPERATOR_NAME2}); + ByteBuffer buffer = ByteBuffer.allocate(testData.length); + buffer.put(testData); + buffer.position(0); + + // Setup expected element. + List<I18Name> nameList = new ArrayList<>(); + nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_OPERATOR_NAME1)); + nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_OPERATOR_NAME2)); + HSFriendlyNameElement expectedElement = new HSFriendlyNameElement(nameList); + + assertEquals(expectedElement, HSFriendlyNameElement.parse(buffer)); + } + + /** + * Verify that an expected HSFriendlyNameElement will be returned when parsing a buffer + * containing a operator name with the maximum length. + * + * @throws Exception + */ + @Test + public void parseBufferWithMaxLengthOperatoreName() throws Exception { + // Operator name with the maximum length. + byte[] textData = new byte[HSFriendlyNameElement.MAXIMUM_OPERATOR_NAME_LENGTH]; + Arrays.fill(textData, (byte) 'a'); + String text = new String(textData); + byte[] testData = getTestData(new String[] {text}); + ByteBuffer buffer = ByteBuffer.allocate(testData.length); + buffer.put(testData); + buffer.position(0); + + // Setup expected element. + List<I18Name> nameList = new ArrayList<>(); + nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, text)); + HSFriendlyNameElement expectedElement = new HSFriendlyNameElement(nameList); + + assertEquals(expectedElement, HSFriendlyNameElement.parse(buffer)); + } + + /** + * Verify that ProtocolException will be thrown when parsing a buffer containing a + * operator name that exceeds the maximum length. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferWithOperatorNameLengthExceedMax() throws Exception { + byte[] textData = new byte[HSFriendlyNameElement.MAXIMUM_OPERATOR_NAME_LENGTH + 1]; + Arrays.fill(textData, (byte) 'a'); + String text = new String(textData); + byte[] testData = getTestData(new String[] {text}); + ByteBuffer buffer = ByteBuffer.allocate(testData.length); + buffer.put(testData); + buffer.position(0); + HSFriendlyNameElement.parse(buffer); + } + +} diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java index 29b196230..6920e581a 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java @@ -16,110 +16,151 @@ package com.android.server.wifi.hotspot2.anqp; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import android.test.suitebuilder.annotation.SmallTest; import org.junit.Test; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.net.ProtocolException; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.Locale; /** - * Unit tests for {@link com.android.server.wifi.anqp.I18Name}. + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.I18Name}. */ @SmallTest public class I18NameTest { - private static class I18NameTestMapping { - byte[] mBytes; - String mExpectedLanguage; - Locale mExpectedLocale; - String mExpectedText; - I18NameTestMapping(byte[] bytes, String language, Locale locale, String text) { - this.mBytes = bytes; - this.mExpectedLanguage = language; - this.mExpectedLocale = locale; - this.mExpectedText = text; - } + private static final String TEST_LANGUAGE = "en"; + private static final Locale TEST_LOCALE = Locale.forLanguageTag(TEST_LANGUAGE); + private static final String TEST_TEXT = "Hello World"; + + /** + * Helper function for returning byte array containing test data. + * + * @param language The language code string + * @param text The text string + * @return byte[] + * @throws IOException + */ + private byte[] getTestData(String language, String text) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + stream.write(language.getBytes(StandardCharsets.US_ASCII)); + stream.write(new byte[]{(byte) 0x0}); // Padding for language code. + stream.write(text.getBytes(StandardCharsets.UTF_8)); + return stream.toByteArray(); } - private static final byte[][] MALFORMED_I18_NAME_BYTES = - new byte[][] { - // Too short. - new byte[0], - new byte[] {(byte) 0x01}, - new byte[] {(byte) 0x01, (byte) 0x02}, - // Length value of 0x02 shorter than the length of the language code field - // (i.e. 3). - new byte[] { - (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0xb0, (byte) 0xb1}, - // Length value of 0xff longer than payload size. - new byte[] { - (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0xb0, (byte) 0xb1} - }; + /** + * Helper function for generating default test data. The test data include the language code + * and text field. + * + * @return byte[] of data + * @throws IOException + */ + private byte[] getDefaultTestData() throws IOException { + return getTestData(TEST_LANGUAGE, TEST_TEXT); + } - private static final I18NameTestMapping[] I18_NAME_MAPPINGS = - new I18NameTestMapping[] { - new I18NameTestMapping( - new byte[] { - (byte) 0x09, (byte) 0x65, (byte) 0x6e, (byte) 0x00, - (byte) 0x74, (byte) 0x65, (byte) 0x73, (byte) 0x74, - (byte) 0x41, (byte) 0x70}, - "en", Locale.ENGLISH, "testAp"), - // TODO: Make sure the ISO-639 alpha-3 code is properly parsed (b/30311144). - /* - new I18NameTestMapping( - new byte[] { - (byte) 0x09, (byte) 0x65, (byte) 0x6e, (byte) 0x67, - (byte) 0x74, (byte) 0x65, (byte) 0x73, (byte) 0x74, - (byte) 0x41, (byte) 0x70}, - "eng", Locale.ENGLISH, "testAp"), - */ - new I18NameTestMapping( - new byte[] { - (byte) 0x0b, (byte) 0x66, (byte) 0x72, (byte) 0x00, - (byte) 0x62, (byte) 0x6c, (byte) 0x61, (byte) 0x68, - (byte) 0x62, (byte) 0x6c, (byte) 0x61, (byte) 0x68}, - "fr", Locale.FRENCH, "blahblah") - }; + /** + * Helper function for returning a buffer containing a I18Name test data. + * + * @Param data The byte array of I18Name data + * @param length The length value to set in the I18Name header + * @return {@link ByteBuffer} + * @throws IOException + */ + private ByteBuffer getTestBuffer(byte[] data, int length) throws IOException { + // Allocate extra byte for storing the length field. + ByteBuffer buffer = ByteBuffer.allocate(data.length + 1); + buffer.put((byte) length); + buffer.put(data); + buffer.position(0); + return buffer; + } /** - * Verifies that parsing malformed I18Name bytes results in a ProtocolException. + * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer. + * + * @throws Exception */ - @Test - public void testMalformedI18NameBytes() { - for (byte[] malformedBytes : MALFORMED_I18_NAME_BYTES) { - try { - I18Name i18Name = new I18Name(ByteBuffer.wrap( - malformedBytes).order(ByteOrder.LITTLE_ENDIAN)); - } catch (ProtocolException e) { - continue; - } - fail("Expected exception while parsing malformed I18 Name bytes: " + malformedBytes); - } + @Test(expected = BufferUnderflowException.class) + public void parseEmptyBuffer() throws Exception { + I18Name.parse(ByteBuffer.allocate(0)); + } + + /** + * Verify that BufferUnderflowException will be thrown when the length field is set to more + * than the actual buffer size. + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseTruncatedBuffer() throws Exception { + byte[] data = getDefaultTestData(); + ByteBuffer buffer = getTestBuffer(data, data.length); + buffer.limit(buffer.remaining() - 1); + I18Name.parse(buffer); } /** - * Verifies that a sampling of valid I18Name bytes are properly parsed. + * Verify that ProtocolException will be thrown when the length field is set to less than + * the minimum. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferWithLengthLessThanMinimum() throws Exception { + byte[] data = getDefaultTestData(); + I18Name.parse(getTestBuffer(data, I18Name.MINIMUM_LENGTH - 1)); + } + + /** + * Verify that the expected I18Name will be returned when parsing a buffer contained the + * predefined test data. + * + * @throws Exception */ @Test - public void testI18NameParsing() { + public void parseBufferWithDefaultTestData() throws Exception { + byte[] data = getDefaultTestData(); + I18Name actualName = I18Name.parse(getTestBuffer(data, data.length)); + I18Name expectedName = new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_TEXT); + assertEquals(expectedName, actualName); + } - for (I18NameTestMapping testMapping : I18_NAME_MAPPINGS) { - try { + /** + * Verify that the expected I18Name will be returned when parsing a buffer contained + * a non-English (French) language. + * + * @throws Exception + */ + @Test + public void parseBufferWithFrenchData() throws Exception { + // Test data for French. + String language = "fr"; + String text = "Hello World"; + byte[] data = getTestData(language, text); + I18Name actualName = I18Name.parse(getTestBuffer(data, data.length)); + I18Name expectedName = new I18Name(language, Locale.forLanguageTag(language), text); + assertEquals(expectedName, actualName); + } - I18Name i18Name = new I18Name(ByteBuffer.wrap( - testMapping.mBytes).order(ByteOrder.LITTLE_ENDIAN)); - assertEquals(testMapping.mExpectedLanguage, i18Name.getLanguage()); - assertEquals(testMapping.mExpectedLocale, i18Name.getLocale()); - assertEquals(testMapping.mExpectedText, i18Name.getText()); - } catch (ProtocolException e) { - fail("Exception encountered during parsing: " + e); - } - } + /** + * Verify that an I18Name with an empty text will be returned when parsing a buffer contained + * an empty text field. + * + * @throws Exception + */ + @Test + public void parseBufferWithEmptyText() throws Exception { + byte[] data = getTestData(TEST_LANGUAGE, ""); + I18Name actualName = I18Name.parse(getTestBuffer(data, data.length)); + I18Name expectedName = new I18Name(TEST_LANGUAGE, TEST_LOCALE, ""); + assertEquals(expectedName, actualName); } } diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java new file mode 100644 index 000000000..bbe814832 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java @@ -0,0 +1,85 @@ +/* + * 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 com.android.server.wifi.hotspot2.anqp; + +import static org.junit.Assert.assertEquals; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.net.ProtocolException; +import java.nio.ByteBuffer; + +/** + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement}. + */ +@SmallTest +public class IPAddressTypeAvailabilityElementTest { + private static final int TEST_IPV4_AVAILABILITY = + IPAddressTypeAvailabilityElement.IPV4_PUBLIC; + private static final int TEST_IPV6_AVAILABILITY = + IPAddressTypeAvailabilityElement.IPV6_AVAILABLE; + + private static int getIPAvailability() { + return (TEST_IPV4_AVAILABILITY << 2) | TEST_IPV6_AVAILABILITY; + } + + /** + * Verify that ProtocolException will be thrown when parsing an empty buffer. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferEmptyBuffer() throws Exception { + IPAddressTypeAvailabilityElement.parse(ByteBuffer.allocate(0)); + } + + /** + * Verify that ProtocolException will be thrown when parsing an buffer containing excess + * data. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferWithExcessData() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate( + IPAddressTypeAvailabilityElement.EXPECTED_BUFFER_LENGTH + 1); + buffer.put((byte) getIPAvailability()); + buffer.put((byte) 0); // Excess data. + buffer.position(0); + IPAddressTypeAvailabilityElement.parse(ByteBuffer.allocate(0)); + } + + /** + * Verify that the expected IPAddressTypeAvailabilityElement is returned when parsing + * a buffer containing the test data. + * + * @throws Exception + */ + @Test + public void parseBufferWithTestData() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate( + IPAddressTypeAvailabilityElement.EXPECTED_BUFFER_LENGTH); + buffer.put((byte) getIPAvailability()); + buffer.position(0); + + IPAddressTypeAvailabilityElement expectedElement = new IPAddressTypeAvailabilityElement( + TEST_IPV4_AVAILABILITY, TEST_IPV6_AVAILABILITY); + assertEquals(expectedElement, IPAddressTypeAvailabilityElement.parse(buffer)); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java new file mode 100644 index 000000000..4e3bd1f9c --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java @@ -0,0 +1,171 @@ +/* + * 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 com.android.server.wifi.hotspot2.anqp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Pair; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.ProtocolException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement}. + */ +@SmallTest +public class RoamingConsortiumElementTest { + // Default test data. Each test data contained a pair indicating the number of bytes for the + // OI and the value of the OI. + private static final Pair<Integer, Long> TEST_OI1 = new Pair<Integer, Long>(1, 0x12L); + private static final Pair<Integer, Long> TEST_OI2 = new Pair<Integer, Long>(2, 0x1234L); + private static final Pair<Integer, Long> TEST_OI3 = new Pair<Integer, Long>(4, 0x12345678L); + private static final Pair<Integer, Long> TEST_OI4 = new Pair<Integer, Long>(8, 0x1234567890L); + + /** + * Helper function for appending an OI field to the given output stream. + * + * @param stream The output stream to write to + * @param OI The OI to write to the output stream + */ + private void appendOI(ByteArrayOutputStream stream, Pair<Integer, Long> oi) { + stream.write(oi.first.byteValue()); + // Write the OI data in big-endian. + for (int i = oi.first.intValue() - 1; i >= 0; i--) { + stream.write((byte) ((oi.second.longValue() >> i * Byte.SIZE) & 0xFF)); + } + } + /** + * Helper function for generating test data with the provided OIs. + * + * @param OIs The OIs to generate the data with + * @return byte[] of data + * @throws IOException + */ + private byte[] getTestData(List<Pair<Integer, Long>> ois) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + for (Pair<Integer, Long> oi : ois) { + appendOI(stream, oi); + } + return stream.toByteArray(); + } + + /** + * Helper function for generating test data using the predefined OIs. + * + * @return byte[] of data + * @throws IOException + */ + private byte[] getDefaultTestData() throws IOException { + List<Pair<Integer, Long>> oiList = new ArrayList<>(); + oiList.add(TEST_OI1); + oiList.add(TEST_OI2); + oiList.add(TEST_OI3); + oiList.add(TEST_OI4); + return getTestData(oiList); + } + + /** + * Helper function for creating a RoamingConsortiumElement using the predefined OIs. + * + * @return {@link RoamingConsortiumElement} + */ + private RoamingConsortiumElement getDefaultElement() { + List<Long> oiList = new ArrayList<>(); + oiList.add(TEST_OI1.second); + oiList.add(TEST_OI2.second); + oiList.add(TEST_OI3.second); + oiList.add(TEST_OI4.second); + return new RoamingConsortiumElement(oiList); + } + + /** + * Verify that no exception will be thrown when parsing an empty buffer and the returned + * RoamingConsortiumElement will contained an empty list of OIs. + * + * @throws Exception + */ + @Test + public void parseEmptyBuffer() throws Exception { + RoamingConsortiumElement element = RoamingConsortiumElement.parse(ByteBuffer.allocate(0)); + assertTrue(element.getOIs().isEmpty()); + } + + /** + * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer + * (missing a byte at the end). + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseTruncatedBuffer() throws Exception { + ByteBuffer buffer = ByteBuffer.wrap(getDefaultTestData()); + buffer.limit(buffer.remaining() - 1); + RoamingConsortiumElement.parse(buffer); + } + + /** + * Verify that an expected RoamingConsortiumElement will be returned when parsing a buffer + * containing valid data. + * + * @throws Exception + */ + @Test + public void parseBufferWithDefaultTestData() throws Exception { + // Setup expected element. + RoamingConsortiumElement expectedElement = getDefaultElement(); + + ByteBuffer buffer = ByteBuffer.wrap(getDefaultTestData()); + assertEquals(expectedElement, RoamingConsortiumElement.parse(buffer)); + } + + /** + * Verify that ProtocolException will be thrown when parsing a buffer contained an OI length + * that's less than minimum allowed. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferWithOILengthLessThanMinimum() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(1); + buffer.put((byte) (RoamingConsortiumElement.MINIMUM_OI_LENGTH - 1)); + buffer.position(0); + RoamingConsortiumElement.parse(buffer); + } + + /** + * Verify that ProtocolException will be thrown when parsing a buffer contained an OI length + * that's more than maximum allowed. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferWithOILengthMoreThanMaximum() throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(1); + buffer.put((byte) (RoamingConsortiumElement.MAXIMUM_OI_LENGTH + 1)); + buffer.position(0); + RoamingConsortiumElement.parse(buffer); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java index 90c3d25be..407e7bfde 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java @@ -16,117 +16,154 @@ package com.android.server.wifi.hotspot2.anqp; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import android.test.suitebuilder.annotation.SmallTest; import org.junit.Test; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.net.ProtocolException; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; /** - * Unit tests for {@link com.android.server.wifi.anqp.VenueNameElement}. + * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.VenueNameElement}. */ @SmallTest public class VenueNameElementTest { - private static class VenueNameElementTestMapping { - byte[] mBytes; - List<I18Name> mExpectedNames; - VenueNameElementTestMapping(byte[] bytes, List<I18Name> names) { - this.mBytes = bytes; - this.mExpectedNames = names; + private static final String TEST_LANGUAGE = "en"; + private static final Locale TEST_LOCALE = Locale.forLanguageTag(TEST_LANGUAGE); + private static final String TEST_VENUE_NAME1 = "Venue1"; + private static final String TEST_VENUE_NAME2 = "Venue2"; + + /** + * Helper function for appending a Venue Name to an output stream. + * + * @param stream Stream to write to + * @param venue The venue name string + * @throws IOException + */ + private void appendVenue(ByteArrayOutputStream stream, String venue) throws IOException { + byte[] venueBytes = venue.getBytes(StandardCharsets.UTF_8); + int length = I18Name.LANGUAGE_CODE_LENGTH + venue.length(); + stream.write((byte) length); + stream.write(TEST_LANGUAGE.getBytes(StandardCharsets.US_ASCII)); + stream.write(new byte[]{(byte) 0x0}); // Padding for language code. + stream.write(venueBytes); + } + + /** + * Helper function for generating test data. + * + * @return byte[] of data + * @throws IOException + */ + private byte[] getTestData(String[] names) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + // Venue info data not currently used. + stream.write(new byte[VenueNameElement.VENUE_INFO_LENGTH]); + for (String name : names) { + appendVenue(stream, name); } + return stream.toByteArray(); } - // Raw bytes, laid out in little endian, that represent malformed Venue Name Element payloads - // that do not conform to IEEE802.11-2012 section 8.4.4.4. - private static final byte[][] MALFORMED_VENUE_NAME_ELEMENT_BYTES = - new byte[][] { - // Too short. - new byte[0], - new byte[] {(byte) 0x01}, - // 1 trailing byte. - new byte[] { - (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x65, - (byte) 0x6e, (byte) 0x00, (byte) 0xab}, - // Length field (0xff) exceeds remaining payload size. - new byte[] { - (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0x65, - (byte) 0x6e, (byte) 0x00, (byte) 0xab}, - }; - - private static final VenueNameElementTestMapping[] VENUE_NAME_ELEMENT_MAPPINGS = - new VenueNameElementTestMapping[] { - // 0 Venue name Duples (i.e. Venue info field only). - new VenueNameElementTestMapping( - new byte[] { - (byte) 0x00, (byte) 0x00}, - new ArrayList<I18Name>()), - // 1 Venue name Duple. - new VenueNameElementTestMapping( - new byte[] { - (byte) 0x00, (byte) 0x00, (byte) 0x0d, (byte) 0x65, - (byte) 0x6e, (byte) 0x00, (byte) 0x74, (byte) 0x65, - (byte) 0x73, (byte) 0x74, (byte) 0x56, (byte) 0x65, - (byte) 0x6e, (byte) 0x75, (byte) 0x65, (byte) 0x31}, - new ArrayList<I18Name>() {{ - add(new I18Name("en", Locale.ENGLISH, "testVenue1")); - }}), - // 2 Venue Name Duples. - new VenueNameElementTestMapping( - new byte[] { - (byte) 0x00, (byte) 0x00, (byte) 0x0d, (byte) 0x65, - (byte) 0x6e, (byte) 0x00, (byte) 0x74, (byte) 0x65, - (byte) 0x73, (byte) 0x74, (byte) 0x56, (byte) 0x65, - (byte) 0x6e, (byte) 0x75, (byte) 0x65, (byte) 0x31, - (byte) 0x0d, (byte) 0x66, (byte) 0x72, (byte) 0x00, - (byte) 0x74, (byte) 0x65, (byte) 0x73, (byte) 0x74, - (byte) 0x56, (byte) 0x65, (byte) 0x6e, (byte) 0x75, - (byte) 0x65, (byte) 0x32}, - new ArrayList<I18Name>() {{ - add(new I18Name("en", Locale.ENGLISH, "testVenue1")); - add(new I18Name("fr", Locale.FRENCH, "testVenue2")); - }}), - }; + /** + * Verify that BufferUnderflowException will be thrown when parsing an empty buffer. + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseEmptyBuffer() throws Exception { + VenueNameElement.parse(ByteBuffer.allocate(0)); + } + + /** + * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer + * (missing a byte at the end). + * + * @throws Exception + */ + @Test(expected = BufferUnderflowException.class) + public void parseTruncatedBuffer() throws Exception { + ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[] {TEST_VENUE_NAME1})); + // Truncate a byte at the end. + buffer.limit(buffer.remaining() - 1); + VenueNameElement.parse(buffer); + } /** - * Verifies that parsing malformed Venue Name Element bytes results in a ProtocolException. + * Verify that a VenueNameElement with empty name list will be returned when parsing a buffer + * contained no venue name (only contained the venue info data). + * + * @throws Exception */ @Test - public void testMalformedVenueNameElementBytes() { - for (byte[] invalidBytes : MALFORMED_VENUE_NAME_ELEMENT_BYTES) { - try { - VenueNameElement venueNameElement = new VenueNameElement( - Constants.ANQPElementType.ANQPVenueName, - ByteBuffer.wrap(invalidBytes).order(ByteOrder.LITTLE_ENDIAN)); - } catch (ProtocolException e) { - continue; - } - fail("Expected exception while parsing malformed Venue Name Element bytes: " - + invalidBytes); - } + public void parseBufferWithEmptyVenueName() throws Exception { + ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[0])); + assertTrue(VenueNameElement.parse(buffer).getNames().isEmpty()); + } + /** + * Verify that an expected VenueNameElement will be returned when parsing a buffer contained + * valid Venue Name data. + * + * @throws Exception + */ + @Test + public void parseBufferWithValidVenueNames() throws Exception { + // Setup expected element. + List<I18Name> nameList = new ArrayList<>(); + nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_VENUE_NAME1)); + nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_VENUE_NAME2)); + VenueNameElement expectedElement = new VenueNameElement(nameList); + + ByteBuffer buffer = ByteBuffer.wrap( + getTestData(new String[] {TEST_VENUE_NAME1, TEST_VENUE_NAME2})); + assertEquals(expectedElement, VenueNameElement.parse(buffer)); } /** - * Verifies that valid Venue Name Element bytes are properly parsed. + * Verify that an expected VenueNameElement will be returned when parsing a buffer + * contained a venue name with the maximum length. + * + * @throws Exception */ @Test - public void testVenueNameElementParsing() { - try { - for (VenueNameElementTestMapping testMapping : VENUE_NAME_ELEMENT_MAPPINGS) { - VenueNameElement venueNameElement = - new VenueNameElement( - Constants.ANQPElementType.ANQPVenueName, - ByteBuffer.wrap(testMapping.mBytes).order(ByteOrder.LITTLE_ENDIAN)); - assertEquals(testMapping.mExpectedNames, venueNameElement.getNames()); - } - } catch (ProtocolException e) { - fail("Exception encountered during parsing: " + e); - } + public void parseBufferWithMaxLengthVenueName() throws Exception { + // Venue name with maximum length. + byte[] textData = new byte[VenueNameElement.MAXIMUM_VENUE_NAME_LENGTH]; + Arrays.fill(textData, (byte) 'a'); + String text = new String(textData); + ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[] {text})); + + // Setup expected element. + List<I18Name> nameList = new ArrayList<>(); + nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, text)); + VenueNameElement expectedElement = new VenueNameElement(nameList); + + assertEquals(expectedElement, VenueNameElement.parse(buffer)); + } + + /** + * Verify that ProtocolException will be thrown when parsing a buffer contained a + * venue name that exceeds the maximum length. + * + * @throws Exception + */ + @Test(expected = ProtocolException.class) + public void parseBufferWithVenueNameLengthExceedMax() throws Exception { + byte[] textData = new byte[VenueNameElement.MAXIMUM_VENUE_NAME_LENGTH + 1]; + Arrays.fill(textData, (byte) 'a'); + String text = new String(textData); + ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[] {text})); + VenueNameElement.parse(buffer); } } |