summaryrefslogtreecommitdiffstats
path: root/luni/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'luni/src/test')
-rw-r--r--luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java33
-rw-r--r--luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java15
-rw-r--r--luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java23
-rw-r--r--luni/src/test/java/libcore/icu/DateIntervalFormatTest.java142
-rw-r--r--luni/src/test/java/libcore/icu/ICUTest.java27
-rw-r--r--luni/src/test/java/libcore/icu/LocaleDataTest.java12
-rw-r--r--luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java667
-rw-r--r--luni/src/test/java/libcore/io/OsTest.java229
-rw-r--r--luni/src/test/java/libcore/java/io/FileDescriptorTest.java6
-rw-r--r--luni/src/test/java/libcore/java/io/FileInputStreamTest.java2
-rw-r--r--luni/src/test/java/libcore/java/io/RandomAccessFileTest.java57
-rw-r--r--luni/src/test/java/libcore/java/lang/FloatTest.java16
-rwxr-xr-x[-rw-r--r--]luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java14
-rw-r--r--luni/src/test/java/libcore/java/lang/OldClassTest.java5
-rw-r--r--luni/src/test/java/libcore/java/lang/OldSystemTest.java8
-rw-r--r--luni/src/test/java/libcore/java/lang/PackageTest.java7
-rw-r--r--luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java2
-rw-r--r--luni/src/test/java/libcore/java/lang/StringTest.java41
-rw-r--r--luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java26
-rw-r--r--luni/src/test/java/libcore/java/lang/package-info.java18
-rw-r--r--luni/src/test/java/libcore/java/lang/reflect/FieldTest.java52
-rw-r--r--luni/src/test/java/libcore/java/lang/reflect/MethodTest.java18
-rw-r--r--luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java5
-rw-r--r--luni/src/test/java/libcore/java/net/InetAddressTest.java86
-rw-r--r--luni/src/test/java/libcore/java/net/InetSocketAddressTest.java32
-rw-r--r--luni/src/test/java/libcore/java/net/OldSocketTest.java27
-rw-r--r--luni/src/test/java/libcore/java/net/SocketTest.java33
-rw-r--r--luni/src/test/java/libcore/java/net/URLConnectionTest.java659
-rw-r--r--luni/src/test/java/libcore/java/nio/channels/SelectorTest.java21
-rw-r--r--luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java69
-rw-r--r--luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java57
-rw-r--r--luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java47
-rw-r--r--luni/src/test/java/libcore/java/security/ProviderTest.java24
-rw-r--r--luni/src/test/java/libcore/java/security/SecureRandomTest.java1
-rw-r--r--luni/src/test/java/libcore/java/security/SignatureTest.java106
-rw-r--r--luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java40
-rw-r--r--luni/src/test/java/libcore/java/security/cert/X509CRLTest.java10
-rw-r--r--luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java7
-rw-r--r--luni/src/test/java/libcore/java/sql/TimestampTest.java8
-rw-r--r--luni/src/test/java/libcore/java/text/BreakIteratorTest.java28
-rw-r--r--luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java14
-rw-r--r--luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java30
-rw-r--r--luni/src/test/java/libcore/java/text/NumberFormatTest.java16
-rw-r--r--luni/src/test/java/libcore/java/util/CalendarTest.java72
-rw-r--r--luni/src/test/java/libcore/java/util/CollectionsTest.java91
-rw-r--r--luni/src/test/java/libcore/java/util/CurrencyTest.java2
-rw-r--r--luni/src/test/java/libcore/java/util/DateTest.java11
-rw-r--r--luni/src/test/java/libcore/java/util/LocaleTest.java91
-rw-r--r--luni/src/test/java/libcore/java/util/OldTimeZoneTest.java14
-rw-r--r--luni/src/test/java/libcore/java/util/TimSortTest.java82
-rw-r--r--luni/src/test/java/libcore/java/util/TimeZoneTest.java46
-rw-r--r--luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java4
-rw-r--r--luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java548
-rw-r--r--luni/src/test/java/libcore/java/util/zip/DeflaterTest.java22
-rw-r--r--luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java36
-rw-r--r--luni/src/test/java/libcore/java/util/zip/InflaterTest.java31
-rw-r--r--luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java102
-rw-r--r--luni/src/test/java/libcore/java/util/zip/Zip64Test.java100
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java34
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipFileTest.java491
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java1
-rw-r--r--luni/src/test/java/libcore/javax/crypto/CipherTest.java638
-rw-r--r--luni/src/test/java/libcore/javax/crypto/ECDHKeyAgreementTest.java23
-rw-r--r--luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java95
-rw-r--r--luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java9
-rw-r--r--luni/src/test/java/libcore/javax/crypto/MacTest.java62
-rw-r--r--luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java87
-rw-r--r--luni/src/test/java/libcore/javax/crypto/MockKey.java2
-rw-r--r--luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java91
-rw-r--r--luni/src/test/java/libcore/javax/crypto/MockMacSpi.java92
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java373
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java115
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java26
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java124
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java2
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java2
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java2
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java2
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java249
-rw-r--r--luni/src/test/java/libcore/net/MimeUtilsTest.java6
-rw-r--r--luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java315
-rw-r--r--luni/src/test/java/libcore/net/http/ResponseUtilsTest.java52
-rw-r--r--luni/src/test/java/libcore/util/HexEncodingTest.java78
-rw-r--r--luni/src/test/java/libcore/util/ZoneInfoDBTest.java7
-rw-r--r--luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java41
-rw-r--r--luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java40
-rw-r--r--luni/src/test/java/org/apache/harmony/security/tests/java/security/Signature2Test.java15
-rw-r--r--luni/src/test/java/tests/security/cert/CertificateTest.java15
-rw-r--r--luni/src/test/java/tests/security/interfaces/DSAPrivateKeyTest.java4
-rw-r--r--luni/src/test/java/tests/security/interfaces/DSAPublicKeyTest.java4
-rw-r--r--luni/src/test/native/libcore_io_Memory_test.cpp134
-rw-r--r--luni/src/test/native/test_openssl_engine.cpp132
-rw-r--r--luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider1
93 files changed, 6063 insertions, 1293 deletions
diff --git a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
index 8708214..fce8507 100644
--- a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
+++ b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
@@ -19,6 +19,7 @@ package com.android.org.bouncycastle.crypto.digests;
import junit.framework.TestCase;
import com.android.org.bouncycastle.crypto.Digest;
import com.android.org.bouncycastle.crypto.ExtendedDigest;
+import tests.util.SummaryStatistics;
/**
* Implements unit tests for our JNI wrapper around OpenSSL. We use the
@@ -36,6 +37,7 @@ public class DigestTest extends TestCase {
* @param newDigest The new digest implementation, provided by OpenSSL
*/
public void doTestMessageDigest(Digest oldDigest, Digest newDigest) {
+ final int WARMUP = 10;
final int ITERATIONS = 100;
byte[] data = new byte[1024];
@@ -54,27 +56,31 @@ public class DigestTest extends TestCase {
data[i] = (byte)i;
}
- long oldTime = 0;
- long newTime = 0;
+ SummaryStatistics oldTime = new SummaryStatistics();
+ SummaryStatistics newTime = new SummaryStatistics();
- for (int j = 0; j < ITERATIONS; j++) {
- long t0 = System.currentTimeMillis();
+ for (int j = 0; j < ITERATIONS + WARMUP; j++) {
+ long t0 = System.nanoTime();
for (int i = 0; i < 4; i++) {
oldDigest.update(data, 0, data.length);
}
int oldLength = oldDigest.doFinal(oldHash, 0);
- long t1 = System.currentTimeMillis();
+ long t1 = System.nanoTime();
- oldTime = oldTime + (t1 - t0);
+ if (j >= WARMUP) {
+ oldTime.add(t1 - t0);
+ }
- long t2 = System.currentTimeMillis();
+ long t2 = System.nanoTime();
for (int i = 0; i < 4; i++) {
newDigest.update(data, 0, data.length);
}
int newLength = newDigest.doFinal(newHash, 0);
- long t3 = System.currentTimeMillis();
+ long t3 = System.nanoTime();
- newTime = newTime + (t3 - t2);
+ if (j >= WARMUP) {
+ newTime.add(t3 - t2);
+ }
assertEquals("Hash sizes must be equal", oldLength, newLength);
@@ -83,10 +89,13 @@ public class DigestTest extends TestCase {
}
}
- System.out.println("Time for " + ITERATIONS + " x old hash processing: " + oldTime + " ms");
- System.out.println("Time for " + ITERATIONS + " x new hash processing: " + newTime + " ms");
+ System.out.println("Time for " + ITERATIONS + " x old hash processing: "
+ + oldTime.toString());
+ System.out.println("Time for " + ITERATIONS + " x new hash processing: "
+ + newTime.toString());
- assertTrue("New hash should be faster", newTime < oldTime);
+ assertTrue("New hash should be faster:\nold=" + oldTime.toString() + "\nnew="
+ + newTime.toString(), newTime.mean() < oldTime.mean());
}
/**
diff --git a/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java b/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
index 8627225..48a175c 100644
--- a/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
+++ b/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
@@ -401,11 +401,6 @@ public class CertBlacklistTest extends TestCase {
assertEquals(bl, getCurrentSerialBlacklist());
}
- public void testTurkTrustIntermediate1SerialBlacklist() throws Exception {
- CertBlacklist bl = new CertBlacklist();
- assertEquals(bl.isSerialNumberBlackListed(createSerialNumber(TURKTRUST_1)), true);
- }
-
public void testTurkTrustIntermediate1PubkeyBlacklist() throws Exception {
// build the public key
PublicKey pk = createPublicKey(TURKTRUST_1);
@@ -417,11 +412,6 @@ public class CertBlacklistTest extends TestCase {
assertEquals(bl.isPublicKeyBlackListed(pk), true);
}
- public void testTurkTrustIntermediate2SerialBlacklist() throws Exception {
- CertBlacklist bl = new CertBlacklist();
- assertEquals(bl.isSerialNumberBlackListed(createSerialNumber(TURKTRUST_2)), true);
- }
-
public void testTurkTrustIntermediate2PubkeyBlacklist() throws Exception {
// build the public key
PublicKey pk = createPublicKey(TURKTRUST_2);
@@ -431,11 +421,6 @@ public class CertBlacklistTest extends TestCase {
assertEquals(bl.isPublicKeyBlackListed(pk), true);
}
- public void testANSSISerialBlacklist() throws Exception {
- CertBlacklist bl = new CertBlacklist();
- assertEquals(bl.isSerialNumberBlackListed(createSerialNumber(ANSSI)), true);
- }
-
public void testANSSIIntermediatePubkeyBlacklist() throws Exception {
// build the public key
PublicKey pk = createPublicKey(ANSSI);
diff --git a/luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java b/luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java
index 9e6d8d7..31e8fc7 100644
--- a/luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java
+++ b/luni/src/test/java/libcore/dalvik/system/PathClassLoaderTest.java
@@ -17,9 +17,12 @@
package libcore.dalvik.system;
import dalvik.system.PathClassLoader;
+import java.lang.reflect.Method;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import libcore.io.Streams;
import junit.framework.TestCase;
public final class PathClassLoaderTest extends TestCase {
@@ -52,6 +55,26 @@ public final class PathClassLoaderTest extends TestCase {
return result;
}
+ public void testAppUseOfPathClassLoader() throws Exception {
+ // Extract loading-test.jar from the resource.
+ ClassLoader pcl = PathClassLoaderTest.class.getClassLoader();
+ File jar = File.createTempFile("loading-test", ".jar");
+ try (InputStream in = pcl.getResourceAsStream("dalvik/system/loading-test.jar");
+ FileOutputStream out = new FileOutputStream(jar)) {
+ Streams.copy(in, out);
+ }
+
+ // Execute code from the jar file using a PathClassLoader.
+ PathClassLoader cl = new PathClassLoader(jar.getPath(), pcl);
+ Class c = cl.loadClass("test.Test1");
+ Method m = c.getMethod("test", (Class[]) null);
+ String result = (String) m.invoke(null, (Object[]) null);
+ assertSame("blort", result);
+
+ // Clean up the extracted jar file.
+ assertTrue(jar.delete());
+ }
+
@Override protected void setUp() throws Exception {
super.setUp();
}
diff --git a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
index 1c1296b..4c22371 100644
--- a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
@@ -16,10 +16,12 @@
package libcore.icu;
-import java.util.Calendar;
-import java.util.Locale;
-import java.util.TimeZone;
-import static libcore.icu.DateIntervalFormat.*;
+import android.icu.util.Calendar;
+import android.icu.util.TimeZone;
+import android.icu.util.ULocale;
+
+import static libcore.icu.DateIntervalFormat.formatDateRange;
+import static libcore.icu.DateUtilsBridge.*;
public class DateIntervalFormatTest extends junit.framework.TestCase {
private static final long MINUTE = 60 * 1000;
@@ -32,7 +34,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
public void test_formatDateInterval() throws Exception {
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
- Calendar c = Calendar.getInstance(tz, Locale.US);
+ Calendar c = Calendar.getInstance(tz, ULocale.US);
c.set(Calendar.MONTH, Calendar.JANUARY);
c.set(Calendar.DAY_OF_MONTH, 19);
c.set(Calendar.HOUR_OF_DAY, 3);
@@ -51,10 +53,10 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
long noonDuration = (8 * 60 + 30) * 60 * 1000 - 15 * 1000;
long midnightDuration = (3 * 60 + 30) * 60 * 1000 + 15 * 1000;
- Locale de_DE = new Locale("de", "DE");
- Locale en_US = new Locale("en", "US");
- Locale es_ES = new Locale("es", "ES");
- Locale es_US = new Locale("es", "US");
+ ULocale de_DE = new ULocale("de", "DE");
+ ULocale en_US = new ULocale("en", "US");
+ ULocale es_ES = new ULocale("es", "ES");
+ ULocale es_US = new ULocale("es", "US");
assertEquals("Monday", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_WEEKDAY));
assertEquals("January 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, FORMAT_SHOW_DATE));
@@ -81,11 +83,11 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
assertEquals("1/19/2009 – 2/9/2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19.1.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19.01.2009 - 22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19.01.2009 - 22.04.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19.01.2009 - 09.02.2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01.2009 – 22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01.2009 – 22.04.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01.2009 – 09.02.2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("1/19/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19/1/2009–22/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19/1/2009–22/4/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19/1/2009–9/2/2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
@@ -114,60 +116,61 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// The same tests but for de_DE.
- assertEquals("19.-22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19.-22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("Mo., 19. - Do., 22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("Montag, 19. - Donnerstag, 22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19.–22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19.–22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mo., 19. – Do., 22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Montag, 19. – Donnerstag, 22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19. Januar - 22. April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19. Jan. - 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("Mo., 19. Jan. - Mi., 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("Januar-April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+ assertEquals("19. Januar – 22. April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19. Jan. – 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mo., 19. Jan. – Mi., 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Januar–April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19. Jan. 2009 - 9. Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("Jan. 2009 - Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19. Januar 2009 - 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("Montag, 19. Januar 2009 - Donnerstag, 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19. Jan. 2009 – 9. Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Jan. 2009 – Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19. Januar 2009 – 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("Montag, 19. Januar 2009 – Donnerstag, 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
// The same tests but for es_US.
- assertEquals("19–22 enero 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19–22 ene. 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene.–jue., 22 ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes, 19 enero–jueves, 22 enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19–22 de enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19 – 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 enero–22 abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 ene.–22 abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene.–mié., 22 abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 de enero–22 de abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 de ene. – 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("enero–abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 ene. de 2009–9 feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("ene. 2009–feb. 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 enero de 2009–9 febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes, 19 enero de 2009–jueves, 9 febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19 de ene. de 2009 – 9 de feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("ene. de 2009 – feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+
+ assertEquals("19 de enero de 2009–9 de febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
// The same tests but for es_ES.
- assertEquals("19–22 enero 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19–22 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene.–jue., 22 ene. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes, 19 enero–jueves, 22 enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+ assertEquals("lun., 19 ene.–jue., 22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 enero–22 abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 ene.–22 abr. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene.–mié., 22 abr. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 de enero–22 de abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 ene.–22 abr. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene.–mié., 22 abr. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("enero–abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 ene. de 2009–9 feb. de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("19 ene. 2009–9 feb. 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
assertEquals("ene. 2009–feb. 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 enero de 2009–9 febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes, 19 enero de 2009–jueves, 9 febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19 de enero de 2009–9 de febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
}
// http://b/8862241 - we should be able to format dates past 2038.
// See also http://code.google.com/p/android/issues/detail?id=13050.
public void test8862241() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
Calendar c = Calendar.getInstance(tz, l);
c.clear();
@@ -181,7 +184,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10089890 - we should take the given time zone into account.
public void test10089890() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL | FORMAT_SHOW_TIME | FORMAT_24HOUR;
@@ -203,7 +206,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
int abbr12 = time12 | FORMAT_ABBREV_ALL;
int abbr24 = time24 | FORMAT_ABBREV_ALL;
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
// Full length on-the-hour times.
@@ -230,7 +233,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10560853 - when the time is not displayed, an end time 0 ms into the next day is
// considered to belong to the previous day.
public void test10560853_when_time_not_displayed() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
long midnight = 0;
@@ -254,7 +257,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10560853 - when the start and end times are otherwise on the same day,
// an end time 0 ms into the next day is considered to belong to the previous day.
public void test10560853_for_single_day_events() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -266,7 +269,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// The fix for http://b/10560853 didn't work except for the day around the epoch, which was
// all the unit test checked!
public void test_single_day_events_later_than_epoch() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -284,7 +287,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// The fix for http://b/10560853 didn't work except for UTC, which was
// all the unit test checked!
public void test_single_day_events_not_in_UTC() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -305,7 +308,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10209343 - even if the caller didn't explicitly ask us to include the year,
// we should do so for years other than the current year.
public void test10209343_when_not_this_year() {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR;
@@ -328,7 +331,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10209343 - for the current year, we should honor the FORMAT_SHOW_YEAR flags.
public void test10209343_when_this_year() {
// Construct a date in the current year (whenever the test happens to be run).
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
Calendar c = Calendar.getInstance(utc, l);
c.set(Calendar.MONTH, Calendar.FEBRUARY);
@@ -363,7 +366,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/8467515 - yet another y2k38 bug report.
public void test8467515() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH | FORMAT_ABBREV_WEEKDAY;
long t;
@@ -383,7 +386,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/12004664
public void test12004664() throws Exception {
TimeZone utc = TimeZone.getTimeZone("UTC");
- Calendar c = Calendar.getInstance(utc, Locale.US);
+ Calendar c = Calendar.getInstance(utc, ULocale.US);
c.clear();
c.set(Calendar.YEAR, 1980);
c.set(Calendar.MONTH, Calendar.FEBRUARY);
@@ -392,26 +395,26 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
long thisYear = c.getTimeInMillis();
int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR;
- assertEquals("Sunday, February 10, 1980", formatDateRange(new Locale("en", "US"), utc, thisYear, thisYear, flags));
+ assertEquals("Sunday, February 10, 1980", formatDateRange(new ULocale("en", "US"), utc, thisYear, thisYear, flags));
- // If we supported non-Gregorian calendars, this is what that we'd expect for these locales.
+ // If we supported non-Gregorian calendars, this is what that we'd expect for these ULocales.
// This is really the correct behavior, but since java.util.Calendar currently only supports
// the Gregorian calendar, we want to deliberately force icu4c to agree, otherwise we'd have
// a mix of calendars throughout an app's UI depending on whether Java or native code formatted
// the date.
- // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه‍.ش.", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
- // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
- // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
+ // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه‍.ش.", formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags));
+ // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags));
+ // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags));
// For now, here are the localized Gregorian strings instead...
- assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
- assertEquals("یکشنبه د ۱۹۸۰ د فبروري ۱۰", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
- assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 1980", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
+ assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags));
+ assertEquals("یکشنبه د ۱۹۸۰ د فبروري ۱۰", formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags));
+ assertEquals("วันอาทิตย์ที่ 10 กุมภาพันธ์ ค.ศ. 1980", formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags));
}
// http://b/13234532
public void test13234532() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR;
@@ -420,4 +423,13 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
assertEquals("11 AM – 1 PM", formatDateRange(l, utc, 11*HOUR, 13*HOUR, flags));
assertEquals("2 – 3 PM", formatDateRange(l, utc, 14*HOUR, 15*HOUR, flags));
}
+
+ // http://b/20708022
+ public void testEndOfDayOnLastDayOfMonth() throws Exception {
+ final ULocale locale = new ULocale("en");
+ final TimeZone timeZone = TimeZone.getTimeZone("UTC");
+
+ assertEquals("11:00 PM – 12:00 AM", formatDateRange(locale, timeZone,
+ 1430434800000L, 1430438400000L, FORMAT_SHOW_TIME));
+ }
}
diff --git a/luni/src/test/java/libcore/icu/ICUTest.java b/luni/src/test/java/libcore/icu/ICUTest.java
index a7cc7a0..5eab070 100644
--- a/luni/src/test/java/libcore/icu/ICUTest.java
+++ b/luni/src/test/java/libcore/icu/ICUTest.java
@@ -20,6 +20,7 @@ import java.text.BreakIterator;
import java.text.Collator;
import java.util.Arrays;
import java.util.Locale;
+import libcore.util.ZoneInfoDB;
public class ICUTest extends junit.framework.TestCase {
public void test_getISOLanguages() throws Exception {
@@ -152,24 +153,24 @@ public class ICUTest extends junit.framework.TestCase {
assertEquals("sr_ME_#Latn", sr_Latn_ME.toString());
assertEquals("Latn", sr_Latn_ME.getScript());
- assertEquals("Српски", sr_Cyrl_BA.getDisplayLanguage(sr_Cyrl_BA));
+ assertEquals("српски", sr_Cyrl_BA.getDisplayLanguage(sr_Cyrl_BA));
assertEquals("Босна и Херцеговина", sr_Cyrl_BA.getDisplayCountry(sr_Cyrl_BA));
- assertEquals("Ћирилица", sr_Cyrl_BA.getDisplayScript(sr_Cyrl_BA));
+ assertEquals("ћирилица", sr_Cyrl_BA.getDisplayScript(sr_Cyrl_BA));
assertEquals("", sr_Cyrl_BA.getDisplayVariant(sr_Cyrl_BA));
- assertEquals("Српски", sr_Cyrl_ME.getDisplayLanguage(sr_Cyrl_ME));
+ assertEquals("српски", sr_Cyrl_ME.getDisplayLanguage(sr_Cyrl_ME));
assertEquals("Црна Гора", sr_Cyrl_ME.getDisplayCountry(sr_Cyrl_ME));
- assertEquals("Ћирилица", sr_Cyrl_ME.getDisplayScript(sr_Cyrl_ME));
+ assertEquals("ћирилица", sr_Cyrl_ME.getDisplayScript(sr_Cyrl_ME));
assertEquals("", sr_Cyrl_ME.getDisplayVariant(sr_Cyrl_ME));
- assertEquals("Srpski", sr_Latn_BA.getDisplayLanguage(sr_Latn_BA));
+ assertEquals("srpski", sr_Latn_BA.getDisplayLanguage(sr_Latn_BA));
assertEquals("Bosna i Hercegovina", sr_Latn_BA.getDisplayCountry(sr_Latn_BA));
- assertEquals("Latinica", sr_Latn_BA.getDisplayScript(sr_Latn_BA));
+ assertEquals("latinica", sr_Latn_BA.getDisplayScript(sr_Latn_BA));
assertEquals("", sr_Latn_BA.getDisplayVariant(sr_Latn_BA));
- assertEquals("Srpski", sr_Latn_ME.getDisplayLanguage(sr_Latn_ME));
+ assertEquals("srpski", sr_Latn_ME.getDisplayLanguage(sr_Latn_ME));
assertEquals("Crna Gora", sr_Latn_ME.getDisplayCountry(sr_Latn_ME));
- assertEquals("Latinica", sr_Latn_ME.getDisplayScript(sr_Latn_ME));
+ assertEquals("latinica", sr_Latn_ME.getDisplayScript(sr_Latn_ME));
assertEquals("", sr_Latn_ME.getDisplayVariant(sr_Latn_ME));
assertEquals("BIH", sr_Cyrl_BA.getISO3Country());
@@ -253,4 +254,14 @@ public class ICUTest extends junit.framework.TestCase {
ICU.setDefaultLocale(initialDefaultLocale);
}
}
+
+ /** Confirms that ICU agrees with the rest of libcore about the version of the TZ data in use. */
+ public void testTimeZoneDataVersion() {
+ String icu4cTzVersion = ICU.getTZDataVersion();
+ String zoneInfoTzVersion = ZoneInfoDB.getInstance().getVersion();
+ assertEquals(icu4cTzVersion, zoneInfoTzVersion);
+
+ String icu4jTzVersion = android.icu.util.TimeZone.getTZDataVersion();
+ assertEquals(icu4jTzVersion, zoneInfoTzVersion);
+ }
}
diff --git a/luni/src/test/java/libcore/icu/LocaleDataTest.java b/luni/src/test/java/libcore/icu/LocaleDataTest.java
index 0a83c53..09c3f0f 100644
--- a/luni/src/test/java/libcore/icu/LocaleDataTest.java
+++ b/luni/src/test/java/libcore/icu/LocaleDataTest.java
@@ -24,7 +24,7 @@ public class LocaleDataTest extends junit.framework.TestCase {
for (Locale l : Locale.getAvailableLocales()) {
LocaleData d = LocaleData.get(l);
// System.err.format("%20s %s %s %s\n", l, d.yesterday, d.today, d.tomorrow);
- // System.err.format("%20s %10s %10s\n", l, d.timeFormat12, d.timeFormat24);
+ // System.err.format("%20s %10s %10s\n", l, d.timeFormat_hm, d.timeFormat_Hm);
}
}
@@ -73,7 +73,7 @@ public class LocaleDataTest extends junit.framework.TestCase {
assertEquals("leden", l.longStandAloneMonthNames[0]);
assertEquals("led", l.shortStandAloneMonthNames[0]);
- assertEquals("l", l.tinyStandAloneMonthNames[0]);
+ assertEquals("1", l.tinyStandAloneMonthNames[0]);
}
public void test_ko_KR() throws Exception {
@@ -124,11 +124,11 @@ public class LocaleDataTest extends junit.framework.TestCase {
// http://b/7924970
public void testTimeFormat12And24() throws Exception {
LocaleData en_US = LocaleData.get(Locale.US);
- assertEquals("h:mm a", en_US.timeFormat12);
- assertEquals("HH:mm", en_US.timeFormat24);
+ assertEquals("h:mm a", en_US.timeFormat_hm);
+ assertEquals("HH:mm", en_US.timeFormat_Hm);
LocaleData ja_JP = LocaleData.get(Locale.JAPAN);
- assertEquals("aK:mm", ja_JP.timeFormat12);
- assertEquals("H:mm", ja_JP.timeFormat24);
+ assertEquals("aK:mm", ja_JP.timeFormat_hm);
+ assertEquals("H:mm", ja_JP.timeFormat_Hm);
}
}
diff --git a/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java b/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
new file mode 100644
index 0000000..101896f
--- /dev/null
+++ b/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2015 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 libcore.icu;
+
+import android.icu.util.ULocale;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_ALL;
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
+import static libcore.icu.DateUtilsBridge.FORMAT_NO_YEAR;
+import static libcore.icu.DateUtilsBridge.FORMAT_NUMERIC_DATE;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_YEAR;
+import static libcore.icu.RelativeDateTimeFormatter.DAY_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.HOUR_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.MINUTE_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.SECOND_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.WEEK_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.YEAR_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.getRelativeDateTimeString;
+import static libcore.icu.RelativeDateTimeFormatter.getRelativeTimeSpanString;
+
+public class RelativeDateTimeFormatterTest extends junit.framework.TestCase {
+
+ // Tests adopted from CTS tests for DateUtils.getRelativeTimeSpanString.
+ public void test_getRelativeTimeSpanStringCTS() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("GMT");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 GMT
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long baseTime = cal.getTimeInMillis();
+
+ assertEquals("0 minutes ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - SECOND_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("In 0 minutes",
+ getRelativeTimeSpanString(en_US, tz, baseTime + SECOND_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+
+ assertEquals("1 minute ago",
+ getRelativeTimeSpanString(en_US, tz, 0, MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 0));
+ assertEquals("In 1 minute",
+ getRelativeTimeSpanString(en_US, tz, MINUTE_IN_MILLIS, 0, MINUTE_IN_MILLIS, 0));
+
+ assertEquals("42 minutes ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - 42 * MINUTE_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("In 42 minutes",
+ getRelativeTimeSpanString(en_US, tz, baseTime + 42 * MINUTE_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+
+ final long TWO_HOURS_IN_MS = 2 * HOUR_IN_MILLIS;
+ assertEquals("2 hours ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - TWO_HOURS_IN_MS, baseTime,
+ MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+ assertEquals("In 2 hours",
+ getRelativeTimeSpanString(en_US, tz, baseTime + TWO_HOURS_IN_MS, baseTime,
+ MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+
+ assertEquals("In 42 min.",
+ getRelativeTimeSpanString(en_US, tz, baseTime + (42 * MINUTE_IN_MILLIS), baseTime,
+ MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+
+ assertEquals("Tomorrow",
+ getRelativeTimeSpanString(en_US, tz, DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+ assertEquals("In 2 days",
+ getRelativeTimeSpanString(en_US, tz, 2 * DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+ assertEquals("Yesterday",
+ getRelativeTimeSpanString(en_US, tz, 0, DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+ assertEquals("2 days ago",
+ getRelativeTimeSpanString(en_US, tz, 0, 2 * DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+
+ final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+ assertEquals("5 days ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+ DAY_IN_MILLIS, 0));
+ }
+
+ private void test_getRelativeTimeSpanString_helper(long delta, long minResolution, int flags,
+ String expectedInPast,
+ String expectedInFuture) throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 PST
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals(expectedInPast,
+ getRelativeTimeSpanString(en_US, tz, base - delta, base, minResolution, flags));
+ assertEquals(expectedInFuture,
+ getRelativeTimeSpanString(en_US, tz, base + delta, base, minResolution, flags));
+ }
+
+ private void test_getRelativeTimeSpanString_helper(long delta, long minResolution,
+ String expectedInPast,
+ String expectedInFuture) throws Exception {
+ test_getRelativeTimeSpanString_helper(delta, minResolution, 0, expectedInPast, expectedInFuture);
+ }
+
+ public void test_getRelativeTimeSpanString() throws Exception {
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, "0 seconds ago", "0 seconds ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", "In 1 minute");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", "In 1 minute");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, "5 days ago", "In 5 days");
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "0 seconds ago",
+ "0 seconds ago");
+ test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 second ago",
+ "In 1 second");
+ test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "2 seconds ago",
+ "In 2 seconds");
+ test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "25 seconds ago",
+ "In 25 seconds");
+ test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+
+ test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "0 minutes ago",
+ "0 minutes ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "2 minutes ago",
+ "In 2 minutes");
+ test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "25 minutes ago",
+ "In 25 minutes");
+ test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "12 hours ago",
+ "In 12 hours");
+
+ test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+ "0 hours ago");
+ test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "2 hours ago",
+ "In 2 hours");
+ test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "5 hours ago",
+ "In 5 hours");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "20 hours ago",
+ "In 20 hours");
+
+ test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, "January 11",
+ "March 2");
+
+ test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+ "0 weeks ago");
+ test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+ test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "2 weeks ago",
+ "In 2 weeks");
+ test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "25 weeks ago",
+ "In 25 weeks");
+
+ // duration >= minResolution
+ test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, "30 seconds ago",
+ "In 30 seconds");
+ test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+ "30 minutes ago", "In 30 minutes");
+ test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, "5 days ago",
+ "In 5 days");
+ test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, "July 10, 2014",
+ "September 3");
+ test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS,
+ "February 6, 2010", "February 4, 2020");
+
+ test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS,
+ "1 minute ago", "In 1 minute");
+ test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+ test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Today");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+ test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+
+ // duration < minResolution
+ test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, "0 minutes ago",
+ "In 0 minutes");
+ test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+ "In 0 hours");
+ test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, "0 hours ago",
+ "In 0 hours");
+ test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+ "In 0 weeks");
+ test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, "0 weeks ago",
+ "In 0 weeks");
+ }
+
+ public void test_getRelativeTimeSpanStringAbbrev() throws Exception {
+ int flags = FORMAT_ABBREV_RELATIVE;
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, flags, "0 sec. ago",
+ "0 sec. ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, flags, "1 min. ago",
+ "In 1 min.");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, flags, "5 days ago", "In 5 days");
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "0 sec. ago", "0 sec. ago");
+ test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 sec. ago", "In 1 sec.");
+ test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "2 sec. ago", "In 2 sec.");
+ test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "25 sec. ago", "In 25 sec.");
+ test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "0 min. ago", "0 min. ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "2 min. ago", "In 2 min.");
+ test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "25 min. ago", "In 25 min.");
+ test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "12 hr. ago", "In 12 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "0 hr. ago");
+ test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "2 hr. ago", "In 2 hr.");
+ test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "5 hr. ago", "In 5 hr.");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "20 hr. ago", "In 20 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+ "Today");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "January 11", "March 2");
+
+ test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "0 wk. ago");
+ test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+ test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "2 wk. ago", "In 2 wk.");
+ test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "25 wk. ago", "In 25 wk.");
+
+ // duration >= minResolution
+ test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, flags, "30 sec. ago",
+ "In 30 sec.");
+ test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "30 min. ago", "In 30 min.");
+ test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "5 days ago", "In 5 days");
+ test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "July 10, 2014", "September 3");
+ test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "February 6, 2010", "February 4, 2020");
+
+ test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+ "Today");
+ test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Today");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+ test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+
+ // duration < minResolution
+ test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "0 min. ago", "In 0 min.");
+ test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "In 0 hr.");
+ test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "In 0 hr.");
+ test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "In 0 wk.");
+ test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "In 0 wk.");
+
+ }
+
+ public void test_getRelativeTimeSpanStringGerman() throws Exception {
+ // Bug: 19744876
+ // We need to specify the timezone and the time explicitly. Otherwise it
+ // may not always give a correct answer of "tomorrow" by using
+ // (now + DAY_IN_MILLIS).
+ Locale de_DE = new Locale("de", "DE");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
+ Calendar cal = Calendar.getInstance(tz, de_DE);
+ // Feb 5, 2015 at 10:50 CET
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long now = cal.getTimeInMillis();
+
+ // 42 minutes ago
+ assertEquals("Vor 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+ now - 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+ // In 42 minutes
+ assertEquals("In 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+ now + 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+ // Yesterday
+ assertEquals("Gestern", getRelativeTimeSpanString(de_DE, tz,
+ now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day before yesterday
+ assertEquals("Vorgestern", getRelativeTimeSpanString(de_DE, tz,
+ now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // Tomorrow
+ assertEquals("Morgen", getRelativeTimeSpanString(de_DE, tz,
+ now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day after tomorrow
+ assertEquals("Übermorgen", getRelativeTimeSpanString(de_DE, tz,
+ now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ }
+
+ public void test_getRelativeTimeSpanStringFrench() throws Exception {
+ Locale fr_FR = new Locale("fr", "FR");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
+ Calendar cal = Calendar.getInstance(tz, fr_FR);
+ // Feb 5, 2015 at 10:50 CET
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long now = cal.getTimeInMillis();
+
+ // 42 minutes ago
+ assertEquals("Il y a 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+ now - (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+ // In 42 minutes
+ assertEquals("Dans 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+ now + (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+ // Yesterday
+ assertEquals("Hier", getRelativeTimeSpanString(fr_FR, tz,
+ now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day before yesterday
+ assertEquals("Avant-hier", getRelativeTimeSpanString(fr_FR, tz,
+ now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // Tomorrow
+ assertEquals("Demain", getRelativeTimeSpanString(fr_FR, tz,
+ now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day after tomorrow
+ assertEquals("Après-demain", getRelativeTimeSpanString(fr_FR, tz,
+ now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ }
+
+ // Tests adopted from CTS tests for DateUtils.getRelativeDateTimeString.
+ public void test_getRelativeDateTimeStringCTS() throws Exception {
+ Locale en_US = Locale.getDefault();
+ TimeZone tz = TimeZone.getDefault();
+ final long baseTime = System.currentTimeMillis();
+
+ final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+ assertNotNull(getRelativeDateTimeString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+ MINUTE_IN_MILLIS, DAY_IN_MILLIS,
+ FORMAT_NUMERIC_DATE));
+ }
+
+ public void test_getRelativeDateTimeString() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 PST
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals("5 seconds ago, 10:49 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("5 min. ago, 10:45 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+ HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("0 hr. ago, 10:45 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 hours ago, 5:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, 0));
+ assertEquals("Yesterday, 7:50 PM",
+ getRelativeDateTimeString(en_US, tz, base - 15 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 days ago, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 7 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ YEAR_IN_MILLIS, 0));
+
+ // User-supplied flags should be ignored when formatting the date clause.
+ final int FORMAT_SHOW_WEEKDAY = 0x00002;
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS,
+ FORMAT_ABBREV_ALL | FORMAT_SHOW_WEEKDAY));
+ }
+
+ public void test_getRelativeDateTimeStringDST() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+
+ // DST starts on Mar 9, 2014 at 2:00 AM.
+ // So 5 hours before 3:15 AM should be formatted as 'Yesterday, 9:15 PM'.
+ cal.set(2014, Calendar.MARCH, 9, 3, 15, 0);
+ long base = cal.getTimeInMillis();
+ assertEquals("Yesterday, 9:15 PM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ // 1 hour after 2:00 AM should be formatted as 'In 1 hour, 4:00 AM'.
+ cal.set(2014, Calendar.MARCH, 9, 2, 0, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("In 1 hour, 4:00 AM",
+ getRelativeDateTimeString(en_US, tz, base + 1 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ // DST ends on Nov 2, 2014 at 2:00 AM. Clocks are turned backward 1 hour to
+ // 1:00 AM. 8 hours before 5:20 AM should be 'Yesterday, 10:20 PM'.
+ cal.set(2014, Calendar.NOVEMBER, 2, 5, 20, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("Yesterday, 10:20 PM",
+ getRelativeDateTimeString(en_US, tz, base - 8 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ cal.set(2014, Calendar.NOVEMBER, 2, 0, 45, 0);
+ base = cal.getTimeInMillis();
+ // 45 minutes after 0:45 AM should be 'In 45 minutes, 1:30 AM'.
+ assertEquals("In 45 minutes, 1:30 AM",
+ getRelativeDateTimeString(en_US, tz, base + 45 * MINUTE_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ // 45 minutes later, it should be 'In 45 minutes, 1:15 AM'.
+ assertEquals("In 45 minutes, 1:15 AM",
+ getRelativeDateTimeString(en_US, tz, base + 90 * MINUTE_IN_MILLIS,
+ base + 45 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+ // Another 45 minutes later, it should be 'In 45 minutes, 2:00 AM'.
+ assertEquals("In 45 minutes, 2:00 AM",
+ getRelativeDateTimeString(en_US, tz, base + 135 * MINUTE_IN_MILLIS,
+ base + 90 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+ }
+
+
+ public void test_getRelativeDateTimeStringItalian() throws Exception {
+ Locale it_IT = new Locale("it", "IT");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Rome");
+ Calendar cal = Calendar.getInstance(tz, it_IT);
+ // 05 febbraio 2015 20:15
+ cal.set(2015, Calendar.FEBRUARY, 5, 20, 15, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals("5 secondi fa, 20:14",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("5 min. fa, 20:10",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+ HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("0 h. fa, 20:10",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("Ieri, 22:15",
+ getRelativeDateTimeString(it_IT, tz, base - 22 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 giorni fa, 20:15",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("27/11/2014, 20:15",
+ getRelativeDateTimeString(it_IT, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ }
+
+ // http://b/5252772: detect the actual date difference
+ public void test5252772() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+
+ // Now is Sep 2, 2011, 10:23 AM PDT.
+ Calendar nowCalendar = Calendar.getInstance(tz, en_US);
+ nowCalendar.set(2011, Calendar.SEPTEMBER, 2, 10, 23, 0);
+ final long now = nowCalendar.getTimeInMillis();
+
+ // Sep 1, 2011, 10:24 AM
+ Calendar yesterdayCalendar1 = Calendar.getInstance(tz, en_US);
+ yesterdayCalendar1.set(2011, Calendar.SEPTEMBER, 1, 10, 24, 0);
+ long yesterday1 = yesterdayCalendar1.getTimeInMillis();
+ assertEquals("Yesterday, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, yesterday1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 1, 2011, 10:22 AM
+ Calendar yesterdayCalendar2 = Calendar.getInstance(tz, en_US);
+ yesterdayCalendar2.set(2011, Calendar.SEPTEMBER, 1, 10, 22, 0);
+ long yesterday2 = yesterdayCalendar2.getTimeInMillis();
+ assertEquals("Yesterday, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, yesterday2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Aug 31, 2011, 10:24 AM
+ Calendar twoDaysAgoCalendar1 = Calendar.getInstance(tz, en_US);
+ twoDaysAgoCalendar1.set(2011, Calendar.AUGUST, 31, 10, 24, 0);
+ long twoDaysAgo1 = twoDaysAgoCalendar1.getTimeInMillis();
+ assertEquals("2 days ago, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysAgo1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Aug 31, 2011, 10:22 AM
+ Calendar twoDaysAgoCalendar2 = Calendar.getInstance(tz, en_US);
+ twoDaysAgoCalendar2.set(2011, Calendar.AUGUST, 31, 10, 22, 0);
+ long twoDaysAgo2 = twoDaysAgoCalendar2.getTimeInMillis();
+ assertEquals("2 days ago, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysAgo2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 3, 2011, 10:22 AM
+ Calendar tomorrowCalendar1 = Calendar.getInstance(tz, en_US);
+ tomorrowCalendar1.set(2011, Calendar.SEPTEMBER, 3, 10, 22, 0);
+ long tomorrow1 = tomorrowCalendar1.getTimeInMillis();
+ assertEquals("Tomorrow, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, tomorrow1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 3, 2011, 10:24 AM
+ Calendar tomorrowCalendar2 = Calendar.getInstance(tz, en_US);
+ tomorrowCalendar2.set(2011, Calendar.SEPTEMBER, 3, 10, 24, 0);
+ long tomorrow2 = tomorrowCalendar2.getTimeInMillis();
+ assertEquals("Tomorrow, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, tomorrow2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 4, 2011, 10:22 AM
+ Calendar twoDaysLaterCalendar1 = Calendar.getInstance(tz, en_US);
+ twoDaysLaterCalendar1.set(2011, Calendar.SEPTEMBER, 4, 10, 22, 0);
+ long twoDaysLater1 = twoDaysLaterCalendar1.getTimeInMillis();
+ assertEquals("In 2 days, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysLater1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 4, 2011, 10:24 AM
+ Calendar twoDaysLaterCalendar2 = Calendar.getInstance(tz, en_US);
+ twoDaysLaterCalendar2.set(2011, Calendar.SEPTEMBER, 4, 10, 24, 0);
+ long twoDaysLater2 = twoDaysLaterCalendar2.getTimeInMillis();
+ assertEquals("In 2 days, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysLater2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+ }
+
+ // b/19822016: show / hide the year based on the dates in the arguments.
+ public void test_bug19822016() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2012 at 10:50 PST
+ cal.set(2012, Calendar.FEBRUARY, 5, 10, 50, 0);
+ long base = cal.getTimeInMillis();
+
+ assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2011, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ assertEquals("January 6, 2012", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+
+ // Feb 5, 2018 at 10:50 PST
+ cal.set(2018, Calendar.FEBRUARY, 5, 10, 50, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2017, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ assertEquals("January 6, 2018", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ }
+}
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index a0d1e5a..9b38ee9 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -16,18 +16,27 @@
package libcore.io;
+import android.system.ErrnoException;
+import android.system.NetlinkSocketAddress;
+import android.system.OsConstants;
+import android.system.PacketSocketAddress;
+import android.system.StructTimeval;
import android.system.StructUcred;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.InetUnixAddress;
+import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Locale;
import junit.framework.TestCase;
import static android.system.OsConstants.*;
@@ -44,6 +53,20 @@ public class OsTest extends TestCase {
s.close();
}
+ public void testFcntlInt() throws Exception {
+ File f = File.createTempFile("OsTest", "tst");
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(f);
+ Libcore.os.fcntlInt(fis.getFD(), F_SETFD, FD_CLOEXEC);
+ int flags = Libcore.os.fcntlVoid(fis.getFD(), F_GETFD);
+ assertTrue((flags & FD_CLOEXEC) != 0);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ f.delete();
+ }
+ }
+
public void testUnixDomainSockets_in_file_system() throws Exception {
String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
new File(path).delete();
@@ -196,9 +219,10 @@ public class OsTest extends TestCase {
fis.close();
}
- public void test_byteBufferPositions_sendto_recvfrom() throws Exception {
- final FileDescriptor serverFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- Libcore.os.bind(serverFd, InetAddress.getLoopbackAddress(), 0);
+ static void checkByteBufferPositions_sendto_recvfrom(
+ int family, InetAddress loopback) throws Exception {
+ final FileDescriptor serverFd = Libcore.os.socket(family, SOCK_STREAM, 0);
+ Libcore.os.bind(serverFd, loopback, 0);
Libcore.os.listen(serverFd, 5);
InetSocketAddress address = (InetSocketAddress) Libcore.os.getsockname(serverFd);
@@ -232,7 +256,7 @@ public class OsTest extends TestCase {
server.start();
- FileDescriptor clientFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+ FileDescriptor clientFd = Libcore.os.socket(family, SOCK_STREAM, 0);
Libcore.os.connect(clientFd, address.getAddress(), address.getPort());
final byte[] bytes = "good bye, cruel black hole with fancy distortion".getBytes(StandardCharsets.US_ASCII);
@@ -254,4 +278,201 @@ public class OsTest extends TestCase {
Libcore.os.close(clientFd);
}
+
+ public void test_NetlinkSocket() throws Exception {
+ FileDescriptor nlSocket = Libcore.os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ Libcore.os.bind(nlSocket, new NetlinkSocketAddress());
+ NetlinkSocketAddress address = (NetlinkSocketAddress) Libcore.os.getsockname(nlSocket);
+ assertTrue(address.getPortId() > 0);
+ assertEquals(0, address.getGroupsMask());
+
+ NetlinkSocketAddress nlKernel = new NetlinkSocketAddress();
+ Libcore.os.connect(nlSocket, nlKernel);
+ NetlinkSocketAddress nlPeer = (NetlinkSocketAddress) Libcore.os.getpeername(nlSocket);
+ assertEquals(0, nlPeer.getPortId());
+ assertEquals(0, nlPeer.getGroupsMask());
+ Libcore.os.close(nlSocket);
+ }
+
+ public void test_PacketSocketAddress() throws Exception {
+ NetworkInterface lo = NetworkInterface.getByName("lo");
+ FileDescriptor fd = Libcore.os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
+ PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, lo.getIndex());
+ Libcore.os.bind(fd, addr);
+
+ PacketSocketAddress bound = (PacketSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals((short) ETH_P_IPV6, bound.sll_protocol); // ETH_P_IPV6 is an int.
+ assertEquals(lo.getIndex(), bound.sll_ifindex);
+ assertEquals(ARPHRD_LOOPBACK, bound.sll_hatype);
+ assertEquals(0, bound.sll_pkttype);
+
+ // The loopback address is ETH_ALEN bytes long and is all zeros.
+ // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
+ assertEquals(6, bound.sll_addr.length);
+ for (int i = 0; i < 6; i++) {
+ assertEquals(0, bound.sll_addr[i]);
+ }
+ }
+
+ public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
+ checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
+ }
+
+ public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
+ checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
+ }
+
+ private void checkSendToSocketAddress(int family, InetAddress loopback) throws Exception {
+ FileDescriptor recvFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
+ Libcore.os.bind(recvFd, loopback, 0);
+ StructTimeval tv = StructTimeval.fromMillis(20);
+ Libcore.os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+
+ InetSocketAddress to = ((InetSocketAddress) Libcore.os.getsockname(recvFd));
+ FileDescriptor sendFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
+ byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
+ int len = msg.length;
+
+ assertEquals(len, Libcore.os.sendto(sendFd, msg, 0, len, 0, to));
+ byte[] received = new byte[msg.length + 42];
+ InetSocketAddress from = new InetSocketAddress();
+ assertEquals(len, Libcore.os.recvfrom(recvFd, received, 0, received.length, 0, from));
+ assertEquals(loopback, from.getAddress());
+ }
+
+ public void test_sendtoSocketAddress_af_inet() throws Exception {
+ checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
+ }
+
+ public void test_sendtoSocketAddress_af_inet6() throws Exception {
+ checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
+ }
+
+ public void test_socketFamilies() throws Exception {
+ FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+ Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
+ InetSocketAddress localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+ fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+ Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+ localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+ fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0);
+ Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+ localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals(Inet4Address.ANY, localSocketAddress.getAddress());
+ try {
+ Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
+ fail("Expected ErrnoException binding IPv4 socket to ::");
+ } catch (ErrnoException expected) {
+ assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT, expected.errno);
+ }
+ }
+
+ private static void assertArrayEquals(byte[] expected, byte[] actual) {
+ assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
+ Arrays.equals(expected, actual));
+ }
+
+ private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
+ byte type, byte responseType, boolean useSendto) throws Exception {
+ int len = packet.length;
+ packet[0] = type;
+ if (useSendto) {
+ assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, to, 0));
+ } else {
+ Libcore.os.connect(fd, to, 0);
+ assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, null, 0));
+ }
+
+ int icmpId = ((InetSocketAddress) Libcore.os.getsockname(fd)).getPort();
+ byte[] received = new byte[4096];
+ InetSocketAddress srcAddress = new InetSocketAddress();
+ assertEquals(len, Libcore.os.recvfrom(fd, received, 0, received.length, 0, srcAddress));
+ assertEquals(to, srcAddress.getAddress());
+ assertEquals(responseType, received[0]);
+ assertEquals(received[4], (byte) (icmpId >> 8));
+ assertEquals(received[5], (byte) (icmpId & 0xff));
+
+ received = Arrays.copyOf(received, len);
+ received[0] = (byte) type;
+ received[2] = received[3] = 0; // Checksum.
+ received[4] = received[5] = 0; // ICMP ID.
+ assertArrayEquals(packet, received);
+ }
+
+ public void test_socketPing() throws Exception {
+ final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
+ final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
+ final byte[] packet = ("\000\000\000\000" + // ICMP type, code.
+ "\000\000\000\003" + // ICMP ID (== port), sequence number.
+ "Hello myself").getBytes(StandardCharsets.US_ASCII);
+
+ FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+ InetAddress ipv6Loopback = InetAddress.getByName("::1");
+ checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true);
+ checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false);
+
+ fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+ InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1");
+ checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
+ checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
+ }
+
+ private static void assertPartial(byte[] expected, byte[] actual) {
+ for (int i = 0; i < expected.length; i++) {
+ if (expected[i] != actual[i]) {
+ fail("Expected " + Arrays.toString(expected) + " but found "
+ + Arrays.toString(actual));
+ }
+ }
+ }
+
+ public void test_xattr() throws Exception {
+ final String NAME_TEST = "user.meow";
+
+ final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+ final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
+
+ File file = File.createTempFile("xattr", "test");
+ String path = file.getAbsolutePath();
+
+ byte[] tmp = new byte[1024];
+ try {
+ try {
+ Libcore.os.getxattr(path, NAME_TEST, tmp);
+ fail("Expected ENODATA");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.ENODATA, e.errno);
+ }
+
+ Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+ assertEquals(VALUE_CAKE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
+ assertPartial(VALUE_CAKE, tmp);
+
+ try {
+ Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
+ fail("Expected EEXIST");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.EEXIST, e.errno);
+ }
+
+ Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
+ assertEquals(VALUE_PIE.length, Libcore.os.getxattr(path, NAME_TEST, tmp));
+ assertPartial(VALUE_PIE, tmp);
+
+ Libcore.os.removexattr(path, NAME_TEST);
+ try {
+ Libcore.os.getxattr(path, NAME_TEST, tmp);
+ fail("Expected ENODATA");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.ENODATA, e.errno);
+ }
+
+ } finally {
+ file.delete();
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/io/FileDescriptorTest.java b/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
index 39472df..e2780c9 100644
--- a/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
+++ b/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
@@ -30,14 +30,14 @@ public class FileDescriptorTest extends TestCase {
new RandomAccessFile(f, "r").getFD().sync();
}
- public void test_isSocket() throws Exception {
+ public void test_isSocket$() throws Exception {
File f = new File("/dev/null");
FileInputStream fis = new FileInputStream(f);
- assertFalse(fis.getFD().isSocket());
+ assertFalse(fis.getFD().isSocket$());
fis.close();
ServerSocket s = new ServerSocket();
- assertTrue(s.getImpl$().getFD$().isSocket());
+ assertTrue(s.getImpl$().getFD$().isSocket$());
s.close();
}
}
diff --git a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
index 14950ee..26de11a 100644
--- a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
@@ -67,7 +67,7 @@ public final class FileInputStreamTest extends TestCase {
}
public void testSkipInPipes() throws Exception {
- FileDescriptor[] pipe = Libcore.os.pipe();
+ FileDescriptor[] pipe = Libcore.os.pipe2(0);
DataFeeder feeder = new DataFeeder(pipe[1]);
try {
feeder.start();
diff --git a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
index afe49b7..8d99457 100644
--- a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
+++ b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
@@ -20,6 +20,8 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+
import junit.framework.TestCase;
import libcore.java.lang.ref.FinalizationTester;
@@ -73,6 +75,61 @@ public final class RandomAccessFileTest extends TestCase {
FinalizationTester.induceFinalization();
}
}
+
+ // http://b/19892782
+ public void testCloseRaf_sameChannelReturned() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+ FileChannel fileChannelBeforeClosing = raf.getChannel();
+ raf.close();
+ FileChannel fileChannelAfterClosing = raf.getChannel();
+ assertSame(fileChannelBeforeClosing, fileChannelAfterClosing);
+ }
+
+ // http://b/19892782
+ public void testCloseRaf_channelIsClosed() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+ FileChannel fileChannelBeforeClosing = raf.getChannel();
+ raf.close();
+ FileChannel fileChannelAfterClosing = raf.getChannel();
+ assertFalse(fileChannelBeforeClosing.isOpen());
+ }
+
+ // http://b/19892782
+ public void testCloseFileChannel_sameChannelReturned() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+ FileChannel fileChannelBeforeClosing = raf.getChannel();
+ fileChannelBeforeClosing.close();
+
+ FileChannel fileChannelAfterClosing = raf.getChannel();
+ assertSame(fileChannelBeforeClosing, fileChannelAfterClosing);
+ }
+
+ // http://b/19892782
+ public void testCloseFileChannel_returnedFileChannelIsClosed() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+ FileChannel fileChannelBeforeClosing = raf.getChannel();
+ // This should close the Raf, and previous implementations wrongly returned a new
+ // open (but useless) channel in this case.
+ fileChannelBeforeClosing.close();
+ FileChannel fileChannelAfterClosing = raf.getChannel();
+ assertFalse(fileChannelBeforeClosing.isOpen());
+ }
+
+ // http://b/19892782
+ public void testCloseRafBeforeGetChannel_returnChannelWithCloseFdAfterClose() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+ raf.close();
+ try {
+ raf.getChannel().size();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
private void createRandomAccessFile(File file) throws Exception {
// TODO: fix our register maps and remove this otherwise unnecessary
// indirection! (http://b/5412580)
diff --git a/luni/src/test/java/libcore/java/lang/FloatTest.java b/luni/src/test/java/libcore/java/lang/FloatTest.java
index 92e7ae4..c25bd5c 100644
--- a/luni/src/test/java/libcore/java/lang/FloatTest.java
+++ b/luni/src/test/java/libcore/java/lang/FloatTest.java
@@ -121,4 +121,20 @@ public class FloatTest extends junit.framework.TestCase {
}
assertEquals(f1, 0f);
}
+
+ // Float equivalent of testParseLargestSubnormalDoublePrecision. http://b/18087920.
+ public void testParseLargestSubnormalFloatPrecision() {
+ // These are different ways of saying MIN_NORMAL.
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.1754943508222875e-38"));
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("0.00011754943508222875e-34f"));
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("00000001.1754943508222875e-38f"));
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.17549435082228750000e-38f"));
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.1754943508222875e-0038f"));
+ assertEquals(-1.1754943508222875e-38f, Float.parseFloat("-1.1754943508222875e-38f"));
+
+ // Extra interesting values suggested as part of http://b/18087920.
+ assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573643E-54"));
+ assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573644E-54"));
+ assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573645E-54"));
+ }
}
diff --git a/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java b/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
index d1704fb..4760c6d 100644..100755
--- a/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
@@ -103,7 +103,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
private class Interrupter extends Thread {
- Waiter waiter;
+ private final Waiter waiter;
Interrupter(String name, Waiter waiter) {
super(name);
@@ -119,8 +119,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
}
- void run_inner() {
- waiter.spin = true;
+ private void run_inner() {
// System.out.println("InterruptTest: starting waiter");
waiter.start();
@@ -168,7 +167,7 @@ public class OldAndroidMonitorTest extends TestCase {
private class Waiter extends Thread {
Object interrupterLock = new Object();
- Boolean spin = false;
+ volatile boolean spin = true;
Waiter(String name) {
super(name);
@@ -188,6 +187,7 @@ public class OldAndroidMonitorTest extends TestCase {
while (spin) {
// We're going to get interrupted while we spin.
}
+
if (interrupted()) {
// System.out.println("Waiter done spinning; interrupted.");
} else {
@@ -196,7 +196,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
synchronized (this) {
- Boolean sawEx = false;
+ boolean sawEx = false;
try {
synchronized (interrupterLock) {
@@ -216,7 +216,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
}
synchronized (this) {
- Boolean sawEx = false;
+ boolean sawEx = false;
try {
synchronized (interrupterLock) {
@@ -236,7 +236,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
}
synchronized (this) {
- Boolean sawEx = false;
+ boolean sawEx = false;
try {
synchronized (interrupterLock) {
diff --git a/luni/src/test/java/libcore/java/lang/OldClassTest.java b/luni/src/test/java/libcore/java/lang/OldClassTest.java
index 23a42bd..f5bc787 100644
--- a/luni/src/test/java/libcore/java/lang/OldClassTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldClassTest.java
@@ -40,11 +40,6 @@ import tests.support.resource.Support_Resources;
@SuppressWarnings("deprecation")
public class OldClassTest extends junit.framework.TestCase {
-
- public static final String FILENAME =
- OldClassTest.class.getPackage().getName().replace('.', '/') +
- "/test#.properties";
-
final String packageName = getClass().getPackage().getName();
final String classNameInitError1 = packageName + ".TestClass1";
final String classNameInitError2 = packageName + ".TestClass1B";
diff --git a/luni/src/test/java/libcore/java/lang/OldSystemTest.java b/luni/src/test/java/libcore/java/lang/OldSystemTest.java
index dee5bdd..93b06c8 100644
--- a/luni/src/test/java/libcore/java/lang/OldSystemTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldSystemTest.java
@@ -260,12 +260,14 @@ public class OldSystemTest extends junit.framework.TestCase {
while(rt.freeMemory() < beforeTest * 2/3) {
vec.add(new StringBuffer(1000));
}
- long beforeGC = rt.freeMemory();
+ long beforeGC = rt.totalMemory() - rt.freeMemory();
+ vec = null;
System.gc();
- long afterGC = rt.freeMemory();
+ System.runFinalization();
+ long afterGC = rt.totalMemory() - rt.freeMemory();
assertTrue("memory was not released after calling System.gc()." +
"before gc: " + beforeGC + "; after gc: " + afterGC,
- beforeGC < afterGC);
+ beforeGC > afterGC);
}
public void test_getenv() {
diff --git a/luni/src/test/java/libcore/java/lang/PackageTest.java b/luni/src/test/java/libcore/java/lang/PackageTest.java
index 6e274a0..c004e23 100644
--- a/luni/src/test/java/libcore/java/lang/PackageTest.java
+++ b/luni/src/test/java/libcore/java/lang/PackageTest.java
@@ -25,9 +25,10 @@ public final class PackageTest extends TestCase {
private static final List<Package> packages = Arrays.asList(Package.getPackages());
public void test_getAnnotations() throws Exception {
- // Package annotations aren't supported, but pre-ICS we crashed.
- assertEquals(0, getClass().getPackage().getAnnotations().length);
- assertEquals(0, getClass().getPackage().getDeclaredAnnotations().length);
+ // Pre-ICS we crashed. To pass, the package-info and TestPackageAnnotation classes must be
+ // on the classpath.
+ assertEquals(1, getClass().getPackage().getAnnotations().length);
+ assertEquals(1, getClass().getPackage().getDeclaredAnnotations().length);
}
public void testGetPackage() {
diff --git a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
index 9766cef..51aed38 100644
--- a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
+++ b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
@@ -28,7 +28,7 @@ import static tests.support.Support_Exec.execAndCheckOutput;
public class ProcessBuilderTest extends AbstractResourceLeakageDetectorTestCase {
private static String shell() {
- String deviceSh = "/system/bin/sh";
+ String deviceSh = System.getenv("ANDROID_ROOT") + "/bin/sh";
String desktopSh = "/bin/sh";
return new File(deviceSh).exists() ? deviceSh : desktopSh;
}
diff --git a/luni/src/test/java/libcore/java/lang/StringTest.java b/luni/src/test/java/libcore/java/lang/StringTest.java
index bf162e5..bd52e06 100644
--- a/luni/src/test/java/libcore/java/lang/StringTest.java
+++ b/luni/src/test/java/libcore/java/lang/StringTest.java
@@ -173,47 +173,6 @@ public class StringTest extends TestCase {
}
/**
- * Tests a widely assumed performance characteristic of String.substring():
- * that it reuses the original's backing array. Although behavior should be
- * correct even if this test fails, many applications may suffer
- * significant performance degradation.
- */
- public void testSubstringSharesBackingArray() throws IllegalAccessException {
- String abcdefghij = "ABCDEFGHIJ";
- String cdefg = abcdefghij.substring(2, 7);
- assertSame(getBackingArray(abcdefghij), getBackingArray(cdefg));
- }
-
- /**
- * Tests a widely assumed performance characteristic of string's copy
- * constructor: that it ensures the backing array is the same length as the
- * string. Although behavior should be correct even if this test fails,
- * many applications may suffer significant performance degradation.
- */
- public void testStringCopiesAvoidHeapRetention() throws IllegalAccessException {
- String abcdefghij = "ABCDEFGHIJ";
- assertSame(getBackingArray(abcdefghij), getBackingArray(new String(abcdefghij)));
-
- String cdefg = abcdefghij.substring(2, 7);
- assertSame(getBackingArray(abcdefghij), getBackingArray(cdefg));
- assertEquals(5, getBackingArray(new String(cdefg)).length);
- }
-
- /**
- * Uses reflection to return the char[] backing the given string. This
- * returns the actual backing array; which must not be modified.
- */
- private char[] getBackingArray(String string) throws IllegalAccessException {
- for (Field f : String.class.getDeclaredFields()) {
- if (!Modifier.isStatic(f.getModifiers()) && f.getType() == char[].class) {
- f.setAccessible(true);
- return (char[]) f.get(string);
- }
- }
- throw new UnsupportedOperationException("No chars[] field on String!");
- }
-
- /**
* Test that strings interned manually and then later loaded as literals
* maintain reference equality. http://b/3098960
*/
diff --git a/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java b/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java
new file mode 100644
index 0000000..7626206
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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 libcore.java.lang;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+// Used by PackageTest
+@Target(ElementType.PACKAGE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TestPackageAnnotation {}
diff --git a/luni/src/test/java/libcore/java/lang/package-info.java b/luni/src/test/java/libcore/java/lang/package-info.java
new file mode 100644
index 0000000..d916e9a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+// Used by PackageTest
+@TestPackageAnnotation
+package libcore.java.lang; \ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java b/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
index b60d984..75665db 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
@@ -17,6 +17,8 @@
package libcore.java.lang.reflect;
import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
import junit.framework.TestCase;
public final class FieldTest extends TestCase {
@@ -46,6 +48,56 @@ public final class FieldTest extends TestCase {
assertFalse(f1.equals(f2));
}
+ // Tests that the "synthetic" modifier is handled correctly.
+ // It's supposed to be present but not shown in toString.
+ public void testSyntheticModifier() throws NoSuchFieldException {
+ Field valuesField = Thread.State.class.getDeclaredField("$VALUES");
+ // Check that this test makes sense.
+ assertTrue(valuesField.isSynthetic());
+ assertEquals(Modifier.SYNTHETIC, valuesField.getModifiers() & Modifier.SYNTHETIC);
+ assertEquals("private static final java.lang.Thread$State[] java.lang.Thread$State.$VALUES",
+ valuesField.toString());
+ }
+
+ // Ensure that the "enum constant" bit is not returned in toString.
+ public void testEnumValueField() throws NoSuchFieldException {
+ Field blockedField = Thread.State.class.getDeclaredField("BLOCKED");
+ assertTrue(Thread.State.class.getDeclaredField("BLOCKED").isEnumConstant());
+ assertEquals("public static final", Modifier.toString(blockedField.getModifiers()));
+ assertEquals(
+ "public static final java.lang.Thread$State java.lang.Thread$State.BLOCKED",
+ blockedField.toString());
+ }
+
+ class ClassWithATransientField {
+ private transient Class<String> transientField = String.class;
+ }
+
+ // Tests that the "transient" modifier is handled correctly.
+ // The underlying constant value for it is the same as for the "varargs" method modifier.
+ // http://b/18488857
+ public void testTransientModifier() throws NoSuchFieldException {
+ Field transientField = ClassWithATransientField.class.getDeclaredField("transientField");
+ // Check that this test makes sense.
+ assertEquals(Modifier.TRANSIENT, transientField.getModifiers() & Modifier.TRANSIENT);
+ assertEquals(
+ "private transient java.lang.Class "
+ + "libcore.java.lang.reflect.FieldTest$ClassWithATransientField"
+ + ".transientField",
+ transientField.toString());
+ }
+
+ public void testToGenericString() throws NoSuchFieldException {
+ Field transientField = ClassWithATransientField.class.getDeclaredField("transientField");
+ // Check that this test makes sense.
+ assertEquals(Modifier.TRANSIENT, transientField.getModifiers() & Modifier.TRANSIENT);
+ assertEquals(
+ "private transient java.lang.Class<java.lang.String> "
+ + "libcore.java.lang.reflect.FieldTest$ClassWithATransientField"
+ + ".transientField",
+ transientField.toGenericString());
+ }
+
static class FieldTestHelper {
public String a;
public Object b;
diff --git a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
index c3a436c..a3f9065 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
@@ -17,6 +17,7 @@
package libcore.java.lang.reflect;
import java.lang.reflect.Method;
+
import junit.framework.TestCase;
public final class MethodTest extends TestCase {
@@ -197,6 +198,23 @@ public final class MethodTest extends TestCase {
assertEquals( "public java.lang.Process java.lang.Runtime.exec(java.lang.String[])"
+ " throws java.io.IOException",
Runtime.class.getMethod("exec", new Class[] { String[].class }).toString());
+ // http://b/18488857
+ assertEquals(
+ "public int java.lang.String.compareTo(java.lang.Object)",
+ String.class.getMethod("compareTo", Object.class).toString());
+ }
+
+ // Tests that the "varargs" modifier is handled correctly.
+ // The underlying constant value for it is the same as for the "transient" field modifier.
+ // http://b/18488857
+ public void testVarargsModifier() throws NoSuchMethodException {
+ Method stringFormatMethod = String.class.getMethod(
+ "format", new Class[] { String.class, Object[].class });
+ assertTrue(stringFormatMethod.isVarArgs());
+ assertEquals(
+ "public static java.lang.String java.lang.String.format("
+ + "java.lang.String,java.lang.Object[])",
+ stringFormatMethod.toString());
}
public static class MethodTestHelper {
diff --git a/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java b/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
index 1bde157..0505f2f 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
@@ -100,6 +100,9 @@ public class ModifierTest extends junit.framework.TestCase {
}
public void test_toStringI() {
- assertEquals("public abstract", Modifier.toString(Modifier.PUBLIC | Modifier.ABSTRACT));
+ // Note that it checks that "STRICT" is rendered as "strictfp" (for other modifiers,
+ // the displayed name is the same as the lowercase constant name).
+ assertEquals("public abstract strictfp",
+ Modifier.toString(Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STRICT));
}
}
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index 4b656cc..8bdcf64 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -21,10 +21,14 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
import libcore.util.SerializationTester;
public class InetAddressTest extends junit.framework.TestCase {
+ private static final byte[] LOOPBACK4_BYTES = new byte[] { 127, 0, 0, 1 };
private static final byte[] LOOPBACK6_BYTES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
private static final String[] INVALID_IPv4_NUMERIC_ADDRESSES = new String[] {
@@ -72,7 +76,7 @@ public class InetAddressTest extends junit.framework.TestCase {
}
private static Inet6Address localhost6() throws Exception {
- return (Inet6Address) InetAddress.getByAddress("localhost", LOOPBACK6_BYTES);
+ return (Inet6Address) InetAddress.getByAddress("ip6-localhost", LOOPBACK6_BYTES);
}
public void test_parseNumericAddress() throws Exception {
@@ -311,4 +315,84 @@ public class InetAddressTest extends junit.framework.TestCase {
assertEquals(resultStrings[i], result);
}
}
+
+ public void test_getHostNameCaches() throws Exception {
+ InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
+ assertEquals("::1", inetAddress.getHostString());
+ assertEquals("ip6-localhost", inetAddress.getHostName());
+ // getHostString() should now be different.
+ assertEquals("ip6-localhost", inetAddress.getHostString());
+ }
+
+ public void test_getByAddress_loopbackIpv4() throws Exception {
+ InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK4_BYTES);
+ assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getByAddress_loopbackIpv6() throws Exception {
+ InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
+ assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getByName_loopbackIpv4() throws Exception {
+ InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
+ assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getByName_loopbackIpv6() throws Exception {
+ InetAddress inetAddress = InetAddress.getByName("::1");
+ assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getAllByName_localhost() throws Exception {
+ InetAddress[] inetAddresses = InetAddress.getAllByName("localhost");
+ assertEquals(1, inetAddresses.length);
+ InetAddress inetAddress = inetAddresses[0];
+ assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getAllByName_ip6_localhost() throws Exception {
+ InetAddress[] inetAddresses = InetAddress.getAllByName("ip6-localhost");
+ assertEquals(1, inetAddresses.length);
+ InetAddress inetAddress = inetAddresses[0];
+ assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getByName_null() throws Exception {
+ InetAddress inetAddress = InetAddress.getByName("::1");
+
+ Set<InetAddress> expectedLoopbackAddresses =
+ createSet(Inet4Address.LOOPBACK, Inet6Address.LOOPBACK);
+ assertTrue(expectedLoopbackAddresses.contains(inetAddress));
+ }
+
+ public void test_getAllByName_null() throws Exception {
+ InetAddress[] inetAddresses = InetAddress.getAllByName(null);
+ assertEquals(2, inetAddresses.length);
+ Set<InetAddress> expectedLoopbackAddresses =
+ createSet(Inet4Address.LOOPBACK, Inet6Address.LOOPBACK);
+ assertEquals(expectedLoopbackAddresses, createSet(inetAddresses));
+ }
+
+ private static void assertEquals(
+ byte[] expectedAddressBytes, String expectedHostname, InetAddress actual) {
+ assertArrayEquals(expectedAddressBytes, actual.getAddress());
+ assertEquals(expectedHostname, actual.getHostName());
+
+ }
+
+ private static void assertArrayEquals(byte[] expected, byte[] actual) {
+ assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
+ Arrays.equals(expected, actual));
+ }
+
+ private static Set<InetAddress> createSet(InetAddress... members) {
+ return new HashSet<InetAddress>(Arrays.asList(members));
+ }
}
diff --git a/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java b/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
index 3bca8dc..d97c48a 100644
--- a/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
@@ -63,7 +63,7 @@ public class InetSocketAddressTest extends TestCase {
}
InetSocketAddress isa = new InetSocketAddress((InetAddress)null, 80);
- assertEquals("0.0.0.0", isa.getHostName());
+ assertEquals("::", isa.getHostName());
try {
new InetSocketAddress(InetAddress.getByName("localhost"), 65536);
@@ -80,7 +80,7 @@ public class InetSocketAddressTest extends TestCase {
public void test_ConstructorI() {
InetSocketAddress isa = new InetSocketAddress(65535);
- assertEquals("0.0.0.0", isa.getHostName());
+ assertEquals("::", isa.getHostName());
assertEquals(65535, isa.getPort());
try {
@@ -150,6 +150,20 @@ public class InetSocketAddressTest extends TestCase {
assertTrue(hasHostname.isUnresolved());
assertEquals("some host", hasHostname.getHostString());
assertEquals("some host", hasHostname.getHostName());
+
+ InetSocketAddress hasHostnameAndAddress = new InetSocketAddress(
+ InetAddress.getByAddress("some host", new byte[] { 127, 0, 0, 1 }),
+ 1234);
+ assertFalse(hasHostnameAndAddress.isUnresolved());
+ assertEquals("some host", hasHostnameAndAddress.getHostString());
+ assertEquals("some host", hasHostnameAndAddress.getHostName());
+
+ // Using a host name that is actually an IP.
+ InetSocketAddress hostnameIsIp = InetSocketAddress.createUnresolved("127.0.0.1", 1234);
+ assertTrue(hostnameIsIp.isUnresolved());
+ assertEquals("127.0.0.1", hostnameIsIp.getHostString());
+ assertEquals("127.0.0.1", hostnameIsIp.getHostName());
+
// When we don't have a hostname, whether or not we do the reverse lookup is the difference
// between getHostString and getHostName...
InetAddress address = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
@@ -157,4 +171,18 @@ public class InetSocketAddressTest extends TestCase {
assertEquals("127.0.0.1", noHostname.getHostString());
assertEquals("localhost", noHostname.getHostName());
}
+
+ public void test_getHostString_cachingBehavior() throws Exception {
+ InetAddress inetAddress = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
+ InetSocketAddress socketAddress = new InetSocketAddress(inetAddress, 1234);
+ assertEquals("127.0.0.1", socketAddress.getHostString());
+ assertEquals("localhost", socketAddress.getHostName());
+ assertEquals("localhost", socketAddress.getHostString());
+
+ inetAddress = InetAddress.getByName("127.0.0.1");
+ socketAddress = new InetSocketAddress(inetAddress, 1234);
+ assertEquals("127.0.0.1", socketAddress.getHostString());
+ assertEquals("localhost", socketAddress.getHostName());
+ assertEquals("localhost", socketAddress.getHostString());
+ }
}
diff --git a/luni/src/test/java/libcore/java/net/OldSocketTest.java b/luni/src/test/java/libcore/java/net/OldSocketTest.java
index 7973965..ded5802 100644
--- a/luni/src/test/java/libcore/java/net/OldSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/OldSocketTest.java
@@ -37,6 +37,7 @@ import java.net.UnknownHostException;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.SocketChannel;
import java.security.Permission;
+import tests.net.StuckServer;
import tests.support.Support_Configuration;
public class OldSocketTest extends OldSocketTestCase {
@@ -932,25 +933,15 @@ public class OldSocketTest extends OldSocketTestCase {
}
// start by validating the error checks
- int portNumber = 0;
- Socket theSocket = null;
- ServerSocket serverSocket = null;
- SocketAddress theAddress = null;
- SocketAddress nonConnectableAddress = null;
- SocketAddress nonReachableAddress = null;
- SocketAddress invalidType = null;
- // byte[] theBytes = {-1,-1,-1,-1};
- byte[] theBytes = { 0, 0, 0, 0 };
- theAddress = new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber);
- nonConnectableAddress = new InetSocketAddress(InetAddress
- .getByAddress(theBytes), portNumber);
- nonReachableAddress = new InetSocketAddress(InetAddress
- .getByName(Support_Configuration.ResolvedNotExistingHost),
- portNumber);
- invalidType = new mySocketAddress();
+ byte[] theBytes = { 0, 0, 0, 0 };
+ SocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0);
+ SocketAddress nonConnectableAddress = new InetSocketAddress(InetAddress.getByAddress(theBytes), 0);
+ SocketAddress nonReachableAddress = new InetSocketAddress(StuckServer.UNREACHABLE_ADDRESS, 0);
+ SocketAddress invalidType = new mySocketAddress();
+ Socket theSocket = null;
+ ServerSocket serverSocket = null;
try {
theSocket = new Socket();
theSocket.connect(null);
@@ -1165,7 +1156,7 @@ public class OldSocketTest extends OldSocketTestCase {
byte[] theBytes = { 0, 0, 0, 0 };
SocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0);
SocketAddress nonConnectableAddress = new InetSocketAddress(InetAddress.getByAddress(theBytes), 0);
- SocketAddress nonReachableAddress = new InetSocketAddress(InetAddress.getByName(Support_Configuration.ResolvedNotExistingHost), 0);
+ SocketAddress nonReachableAddress = new InetSocketAddress(StuckServer.UNREACHABLE_ADDRESS, 0);
SocketAddress invalidType = new mySocketAddress();
Socket theSocket = null;
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index fb09be0..9765a45 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -31,9 +31,11 @@ import java.net.SocketImpl;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
public class SocketTest extends junit.framework.TestCase {
// See http://b/2980559.
@@ -353,6 +355,37 @@ public class SocketTest extends junit.framework.TestCase {
assertEquals(boundAddress.getPort(), localAddressAfterClose.getPort());
}
+ public void testCloseDuringConnect() throws Exception {
+ final CountDownLatch signal = new CountDownLatch(1);
+
+ final Socket s = new Socket();
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ // This address is reserved for documentation: should never be reachable.
+ InetSocketAddress unreachableIp = new InetSocketAddress("192.0.2.0", 80);
+ // This should never return.
+ s.connect(unreachableIp, 0 /* infinite */);
+ fail("Connect returned unexpectedly for: " + unreachableIp);
+ } catch (SocketException expected) {
+ assertTrue(expected.getMessage().contains("Socket closed"));
+ signal.countDown();
+ } catch (IOException e) {
+ fail("Unexpected exception: " + e);
+ }
+ }
+ }.start();
+
+ // Wait for the connect() thread to run and start connect()
+ Thread.sleep(2000);
+
+ s.close();
+
+ boolean connectUnblocked = signal.await(2000, TimeUnit.MILLISECONDS);
+ assertTrue(connectUnblocked);
+ }
+
static class MockServer {
private ExecutorService executor;
private ServerSocket serverSocket;
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index c09939f..3f831e0 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -16,7 +16,8 @@
package libcore.java.net;
-import com.android.okhttp.HttpResponseCache;
+import com.android.okhttp.AndroidShimResponseCache;
+
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
import com.google.mockwebserver.RecordedRequest;
@@ -37,11 +38,14 @@ import java.net.ProtocolException;
import java.net.Proxy;
import java.net.ResponseCache;
import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
+import java.nio.channels.SocketChannel;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -58,18 +62,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
-import javax.net.SocketFactory;
+import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
-import libcore.java.security.StandardNames;
import libcore.java.security.TestKeyStore;
import libcore.java.util.AbstractResourceLeakageDetectorTestCase;
import libcore.javax.net.ssl.TestSSLContext;
@@ -84,7 +88,7 @@ import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
public final class URLConnectionTest extends AbstractResourceLeakageDetectorTestCase {
private MockWebServer server;
- private HttpResponseCache cache;
+ private AndroidShimResponseCache cache;
private String hostName;
@Override protected void setUp() throws Exception {
@@ -674,11 +678,144 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
private void initResponseCache() throws IOException {
String tmp = System.getProperty("java.io.tmpdir");
File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
- cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
+ cache = AndroidShimResponseCache.create(cacheDir, Integer.MAX_VALUE);
ResponseCache.setDefault(cache);
}
/**
+ * Test Etag headers are returned correctly when a client-side cache is not installed.
+ * https://code.google.com/p/android/issues/detail?id=108949
+ */
+ public void testEtagHeaders_uncached() throws Exception {
+ final String etagValue1 = "686897696a7c876b7e";
+ final String body1 = "Response with etag 1";
+ final String etagValue2 = "686897696a7c876b7f";
+ final String body2 = "Response with etag 2";
+
+ server.enqueue(
+ new MockResponse()
+ .setBody(body1)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue1));
+ server.enqueue(
+ new MockResponse()
+ .setBody(body2)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue2));
+ server.play();
+
+ URL url = server.getUrl("/");
+ HttpURLConnection connection1 = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue1, connection1.getHeaderField("Etag"));
+ assertContent(body1, connection1);
+ connection1.disconnect();
+
+ // Discard the server-side record of the request made.
+ server.takeRequest();
+
+ HttpURLConnection connection2 = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue2, connection2.getHeaderField("Etag"));
+ assertContent(body2, connection2);
+ connection2.disconnect();
+
+ // Check the client did not cache.
+ RecordedRequest request = server.takeRequest();
+ assertNull(request.getHeader("If-None-Match"));
+ }
+
+ /**
+ * Test Etag headers are returned correctly when a client-side cache is installed and the server
+ * data is unchanged.
+ * https://code.google.com/p/android/issues/detail?id=108949
+ */
+ public void testEtagHeaders_cachedWithServerHit() throws Exception {
+ final String etagValue = "686897696a7c876b7e";
+ final String body = "Response with etag";
+
+ server.enqueue(
+ new MockResponse()
+ .setBody(body)
+ .setResponseCode(HttpURLConnection.HTTP_OK)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue));
+
+ server.enqueue(
+ new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.play();
+
+ initResponseCache();
+
+ URL url = server.getUrl("/");
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue, connection.getHeaderField("Etag"));
+ assertContent(body, connection);
+ connection.disconnect();
+
+ // Discard the server-side record of the request made.
+ server.takeRequest();
+
+ // Confirm the cached body is returned along with the original etag header.
+ HttpURLConnection cachedConnection = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue, cachedConnection.getHeaderField("Etag"));
+ assertContent(body, cachedConnection);
+ cachedConnection.disconnect();
+
+ // Check the client formatted the request correctly.
+ RecordedRequest request = server.takeRequest();
+ assertEquals(etagValue, request.getHeader("If-None-Match"));
+ }
+
+ /**
+ * Test Etag headers are returned correctly when a client-side cache is installed and the server
+ * data has changed.
+ * https://code.google.com/p/android/issues/detail?id=108949
+ */
+ public void testEtagHeaders_cachedWithServerMiss() throws Exception {
+ final String etagValue1 = "686897696a7c876b7e";
+ final String body1 = "Response with etag 1";
+ final String etagValue2 = "686897696a7c876b7f";
+ final String body2 = "Response with etag 2";
+
+ server.enqueue(
+ new MockResponse()
+ .setBody(body1)
+ .setResponseCode(HttpURLConnection.HTTP_OK)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue1));
+
+ server.enqueue(
+ new MockResponse()
+ .setBody(body2)
+ .setResponseCode(HttpURLConnection.HTTP_OK)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue2));
+
+ server.play();
+
+ initResponseCache();
+
+ URL url = server.getUrl("/");
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue1, connection.getHeaderField("Etag"));
+ assertContent(body1, connection);
+ connection.disconnect();
+
+ // Discard the server-side record of the request made.
+ server.takeRequest();
+
+ // Confirm the new body is returned along with the new etag header.
+ HttpURLConnection cachedConnection = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue2, cachedConnection.getHeaderField("Etag"));
+ assertContent(body2, cachedConnection);
+ cachedConnection.disconnect();
+
+ // Check the client formatted the request correctly.
+ RecordedRequest request = server.takeRequest();
+ assertEquals(etagValue1, request.getHeader("If-None-Match"));
+ }
+
+ /**
* Test which headers are sent unencrypted to the HTTP proxy.
*/
public void testProxyConnectIncludesProxyHeadersOnly()
@@ -2074,6 +2211,19 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
connection.disconnect();
}
+ public void testLastModified() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Last-Modified", "Wed, 27 Nov 2013 11:26:00 GMT")
+ .setBody("Hello"));
+ server.play();
+
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.connect();
+
+ assertEquals(1385551560000L, connection.getLastModified());
+ assertEquals(1385551560000L, connection.getHeaderFieldDate("Last-Modified", -1));
+ }
+
public void testClientSendsContentLength() throws Exception {
server.enqueue(new MockResponse().setBody("A"));
server.play();
@@ -2185,52 +2335,107 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
urlConnection.getInputStream();
}
- public void testSslFallback() throws Exception {
+ public void testSslFallback_allSupportedProtocols() throws Exception {
TestSSLContext testSSLContext = TestSSLContext.create();
- // This server socket factory only supports SSLv3. This is to avoid issues due to SCSV
- // checks. See https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00
+ String[] allSupportedProtocols = { "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" };
SSLSocketFactory serverSocketFactory =
new LimitedProtocolsSocketFactory(
testSSLContext.serverContext.getSocketFactory(),
- "SSLv3");
-
+ allSupportedProtocols);
server.useHttps(serverSocketFactory, false);
server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
- server.enqueue(new MockResponse().setBody("This required a 2nd handshake"));
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setBody("This required fallbacks"));
server.play();
HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
- // Keep track of the client sockets created so that we can interrogate them.
- RecordingSocketFactory clientSocketFactory =
- new RecordingSocketFactory(testSSLContext.clientContext.getSocketFactory());
+ // Keeps track of the client sockets created so that we can interrogate them.
+ final boolean disableFallbackScsv = true;
+ FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory(
+ new LimitedProtocolsSocketFactory(
+ testSSLContext.clientContext.getSocketFactory(), allSupportedProtocols),
+ disableFallbackScsv);
connection.setSSLSocketFactory(clientSocketFactory);
- assertEquals("This required a 2nd handshake",
+ assertEquals("This required fallbacks",
readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+ // Confirm the server accepted a single connection.
RecordedRequest retry = server.takeRequest();
assertEquals(0, retry.getSequenceNumber());
assertEquals("SSLv3", retry.getSslProtocol());
// Confirm the client fallback looks ok.
List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets();
- assertEquals(2, createdSockets.size());
- SSLSocket clientSocket1 = createdSockets.get(0);
- List<String> clientSocket1EnabledProtocols = Arrays.asList(
- clientSocket1.getEnabledProtocols());
- assertContains(clientSocket1EnabledProtocols, "TLSv1.2");
- List<String> clientSocket1EnabledCiphers =
- Arrays.asList(clientSocket1.getEnabledCipherSuites());
- assertContainsNoneMatching(
- clientSocket1EnabledCiphers, StandardNames.CIPHER_SUITE_FALLBACK);
-
- SSLSocket clientSocket2 = createdSockets.get(1);
- List<String> clientSocket2EnabledProtocols =
- Arrays.asList(clientSocket2.getEnabledProtocols());
- assertContainsNoneMatching(clientSocket2EnabledProtocols, "TLSv1.2");
- List<String> clientSocket2EnabledCiphers =
- Arrays.asList(clientSocket2.getEnabledCipherSuites());
- assertContains(clientSocket2EnabledCiphers, StandardNames.CIPHER_SUITE_FALLBACK);
+ assertEquals(4, createdSockets.size());
+ TlsFallbackDisabledScsvSSLSocket clientSocket1 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0);
+ assertSslSocket(clientSocket1,
+ false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket2 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1);
+ assertSslSocket(clientSocket2,
+ true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1", "SSLv3");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket3 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2);
+ assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1", "SSLv3");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket4 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(3);
+ assertSslSocket(clientSocket4, true /* expectedWasFallbackScsvSet */, "SSLv3");
+ }
+
+ public void testSslFallback_defaultProtocols() throws Exception {
+ TestSSLContext testSSLContext = TestSSLContext.create();
+
+ server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setBody("This required fallbacks"));
+ server.play();
+
+ HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
+ // Keeps track of the client sockets created so that we can interrogate them.
+ final boolean disableFallbackScsv = true;
+ FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory(
+ testSSLContext.clientContext.getSocketFactory(),
+ disableFallbackScsv);
+ connection.setSSLSocketFactory(clientSocketFactory);
+ assertEquals("This required fallbacks",
+ readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+
+ // Confirm the server accepted a single connection.
+ RecordedRequest retry = server.takeRequest();
+ assertEquals(0, retry.getSequenceNumber());
+ assertEquals("TLSv1", retry.getSslProtocol());
+
+ // Confirm the client fallback looks ok.
+ List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets();
+ assertEquals(3, createdSockets.size());
+ TlsFallbackDisabledScsvSSLSocket clientSocket1 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0);
+ assertSslSocket(clientSocket1,
+ false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket2 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1);
+ assertSslSocket(clientSocket2, true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket3 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2);
+ assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1");
+ }
+
+ private static void assertSslSocket(TlsFallbackDisabledScsvSSLSocket socket,
+ boolean expectedWasFallbackScsvSet, String... expectedEnabledProtocols) {
+ Set<String> enabledProtocols =
+ new HashSet<String>(Arrays.asList(socket.getEnabledProtocols()));
+ Set<String> expectedProtocolsSet = new HashSet<String>(Arrays.asList(expectedEnabledProtocols));
+ assertEquals(expectedProtocolsSet, enabledProtocols);
+ assertEquals(expectedWasFallbackScsvSet, socket.wasTlsFallbackScsvSet());
}
public void testInspectSslBeforeConnect() throws Exception {
@@ -2491,36 +2696,37 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
@Override
- public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+ public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
- return delegate.createSocket(s, host, port, autoClose);
+ return (SSLSocket) delegate.createSocket(s, host, port, autoClose);
}
@Override
- public Socket createSocket() throws IOException {
- return delegate.createSocket();
+ public SSLSocket createSocket() throws IOException {
+ return (SSLSocket) delegate.createSocket();
}
@Override
- public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
- return delegate.createSocket(host, port);
+ public SSLSocket createSocket(String host, int port)
+ throws IOException, UnknownHostException {
+ return (SSLSocket) delegate.createSocket(host, port);
}
@Override
- public Socket createSocket(String host, int port, InetAddress localHost,
+ public SSLSocket createSocket(String host, int port, InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
- return delegate.createSocket(host, port, localHost, localPort);
+ return (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
}
@Override
- public Socket createSocket(InetAddress host, int port) throws IOException {
- return delegate.createSocket(host, port);
+ public SSLSocket createSocket(InetAddress host, int port) throws IOException {
+ return (SSLSocket) delegate.createSocket(host, port);
}
@Override
- public Socket createSocket(InetAddress address, int port,
+ public SSLSocket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
- return delegate.createSocket(address, port, localAddress, localPort);
+ return (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
}
}
@@ -2539,7 +2745,7 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
@Override
- public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+ public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
SSLSocket socket = (SSLSocket) delegate.createSocket(s, host, port, autoClose);
socket.setEnabledProtocols(protocols);
@@ -2547,21 +2753,22 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
@Override
- public Socket createSocket() throws IOException {
+ public SSLSocket createSocket() throws IOException {
SSLSocket socket = (SSLSocket) delegate.createSocket();
socket.setEnabledProtocols(protocols);
return socket;
}
@Override
- public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+ public SSLSocket createSocket(String host, int port)
+ throws IOException, UnknownHostException {
SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
socket.setEnabledProtocols(protocols);
return socket;
}
@Override
- public Socket createSocket(String host, int port, InetAddress localHost,
+ public SSLSocket createSocket(String host, int port, InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
socket.setEnabledProtocols(protocols);
@@ -2569,14 +2776,14 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
@Override
- public Socket createSocket(InetAddress host, int port) throws IOException {
+ public SSLSocket createSocket(InetAddress host, int port) throws IOException {
SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
socket.setEnabledProtocols(protocols);
return socket;
}
@Override
- public Socket createSocket(InetAddress address, int port,
+ public SSLSocket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
SSLSocket socket =
(SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
@@ -2586,58 +2793,337 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
/**
- * An SSLSocketFactory that delegates calls and keeps a record of any sockets created.
+ * An {@link javax.net.ssl.SSLSocket} that delegates all calls.
*/
- private static class RecordingSocketFactory extends DelegatingSSLSocketFactory {
+ private static abstract class DelegatingSSLSocket extends SSLSocket {
+ protected final SSLSocket delegate;
+
+ public DelegatingSSLSocket(SSLSocket delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override public void shutdownInput() throws IOException {
+ delegate.shutdownInput();
+ }
+
+ @Override public void shutdownOutput() throws IOException {
+ delegate.shutdownOutput();
+ }
+
+ @Override public String[] getSupportedCipherSuites() {
+ return delegate.getSupportedCipherSuites();
+ }
+
+ @Override public String[] getEnabledCipherSuites() {
+ return delegate.getEnabledCipherSuites();
+ }
+
+ @Override public void setEnabledCipherSuites(String[] suites) {
+ delegate.setEnabledCipherSuites(suites);
+ }
+
+ @Override public String[] getSupportedProtocols() {
+ return delegate.getSupportedProtocols();
+ }
+
+ @Override public String[] getEnabledProtocols() {
+ return delegate.getEnabledProtocols();
+ }
+
+ @Override public void setEnabledProtocols(String[] protocols) {
+ delegate.setEnabledProtocols(protocols);
+ }
+
+ @Override public SSLSession getSession() {
+ return delegate.getSession();
+ }
+
+ @Override public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+ delegate.addHandshakeCompletedListener(listener);
+ }
+
+ @Override public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+ delegate.removeHandshakeCompletedListener(listener);
+ }
+
+ @Override public void startHandshake() throws IOException {
+ delegate.startHandshake();
+ }
+
+ @Override public void setUseClientMode(boolean mode) {
+ delegate.setUseClientMode(mode);
+ }
+
+ @Override public boolean getUseClientMode() {
+ return delegate.getUseClientMode();
+ }
+
+ @Override public void setNeedClientAuth(boolean need) {
+ delegate.setNeedClientAuth(need);
+ }
+
+ @Override public void setWantClientAuth(boolean want) {
+ delegate.setWantClientAuth(want);
+ }
+
+ @Override public boolean getNeedClientAuth() {
+ return delegate.getNeedClientAuth();
+ }
+
+ @Override public boolean getWantClientAuth() {
+ return delegate.getWantClientAuth();
+ }
+ @Override public void setEnableSessionCreation(boolean flag) {
+ delegate.setEnableSessionCreation(flag);
+ }
+
+ @Override public boolean getEnableSessionCreation() {
+ return delegate.getEnableSessionCreation();
+ }
+
+ @Override public SSLParameters getSSLParameters() {
+ return delegate.getSSLParameters();
+ }
+
+ @Override public void setSSLParameters(SSLParameters p) {
+ delegate.setSSLParameters(p);
+ }
+
+ @Override public void close() throws IOException {
+ delegate.close();
+ }
+
+ @Override public InetAddress getInetAddress() {
+ return delegate.getInetAddress();
+ }
+
+ @Override public InputStream getInputStream() throws IOException {
+ return delegate.getInputStream();
+ }
+
+ @Override public boolean getKeepAlive() throws SocketException {
+ return delegate.getKeepAlive();
+ }
+
+ @Override public InetAddress getLocalAddress() {
+ return delegate.getLocalAddress();
+ }
+
+ @Override public int getLocalPort() {
+ return delegate.getLocalPort();
+ }
+
+ @Override public OutputStream getOutputStream() throws IOException {
+ return delegate.getOutputStream();
+ }
+
+ @Override public int getPort() {
+ return delegate.getPort();
+ }
+
+ @Override public int getSoLinger() throws SocketException {
+ return delegate.getSoLinger();
+ }
+
+ @Override public int getReceiveBufferSize() throws SocketException {
+ return delegate.getReceiveBufferSize();
+ }
+
+ @Override public int getSendBufferSize() throws SocketException {
+ return delegate.getSendBufferSize();
+ }
+
+ @Override public int getSoTimeout() throws SocketException {
+ return delegate.getSoTimeout();
+ }
+
+ @Override public boolean getTcpNoDelay() throws SocketException {
+ return delegate.getTcpNoDelay();
+ }
+
+ @Override public void setKeepAlive(boolean keepAlive) throws SocketException {
+ delegate.setKeepAlive(keepAlive);
+ }
+
+ @Override public void setSendBufferSize(int size) throws SocketException {
+ delegate.setSendBufferSize(size);
+ }
+
+ @Override public void setReceiveBufferSize(int size) throws SocketException {
+ delegate.setReceiveBufferSize(size);
+ }
+
+ @Override public void setSoLinger(boolean on, int timeout) throws SocketException {
+ delegate.setSoLinger(on, timeout);
+ }
+
+ @Override public void setSoTimeout(int timeout) throws SocketException {
+ delegate.setSoTimeout(timeout);
+ }
+
+ @Override public void setTcpNoDelay(boolean on) throws SocketException {
+ delegate.setTcpNoDelay(on);
+ }
+
+ @Override public String toString() {
+ return delegate.toString();
+ }
+
+ @Override public SocketAddress getLocalSocketAddress() {
+ return delegate.getLocalSocketAddress();
+ }
+
+ @Override public SocketAddress getRemoteSocketAddress() {
+ return delegate.getRemoteSocketAddress();
+ }
+
+ @Override public boolean isBound() {
+ return delegate.isBound();
+ }
+
+ @Override public boolean isConnected() {
+ return delegate.isConnected();
+ }
+
+ @Override public boolean isClosed() {
+ return delegate.isClosed();
+ }
+
+ @Override public void bind(SocketAddress localAddr) throws IOException {
+ delegate.bind(localAddr);
+ }
+
+ @Override public void connect(SocketAddress remoteAddr) throws IOException {
+ delegate.connect(remoteAddr);
+ }
+
+ @Override public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
+ delegate.connect(remoteAddr, timeout);
+ }
+
+ @Override public boolean isInputShutdown() {
+ return delegate.isInputShutdown();
+ }
+
+ @Override public boolean isOutputShutdown() {
+ return delegate.isOutputShutdown();
+ }
+
+ @Override public void setReuseAddress(boolean reuse) throws SocketException {
+ delegate.setReuseAddress(reuse);
+ }
+
+ @Override public boolean getReuseAddress() throws SocketException {
+ return delegate.getReuseAddress();
+ }
+
+ @Override public void setOOBInline(boolean oobinline) throws SocketException {
+ delegate.setOOBInline(oobinline);
+ }
+
+ @Override public boolean getOOBInline() throws SocketException {
+ return delegate.getOOBInline();
+ }
+
+ @Override public void setTrafficClass(int value) throws SocketException {
+ delegate.setTrafficClass(value);
+ }
+
+ @Override public int getTrafficClass() throws SocketException {
+ return delegate.getTrafficClass();
+ }
+
+ @Override public void sendUrgentData(int value) throws IOException {
+ delegate.sendUrgentData(value);
+ }
+
+ @Override public SocketChannel getChannel() {
+ return delegate.getChannel();
+ }
+
+ @Override public void setPerformancePreferences(int connectionTime, int latency,
+ int bandwidth) {
+ delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
+ }
+ }
+
+ /**
+ * An SSLSocketFactory that delegates calls. It keeps a record of any sockets created.
+ * If {@link #disableTlsFallbackScsv} is set to {@code true} then sockets created by the
+ * delegate are wrapped with ones that will not accept the {@link #TLS_FALLBACK_SCSV} cipher,
+ * thus bypassing server-side fallback checks on platforms that support it. Unfortunately this
+ * wrapping will disable any reflection-based calls to SSLSocket from Platform.
+ */
+ private static class FallbackTestClientSocketFactory extends DelegatingSSLSocketFactory {
+ /**
+ * The cipher suite used during TLS connection fallback to indicate a fallback.
+ * See https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00
+ */
+ public static final String TLS_FALLBACK_SCSV = "TLS_FALLBACK_SCSV";
+
+ private final boolean disableTlsFallbackScsv;
private final List<SSLSocket> createdSockets = new ArrayList<SSLSocket>();
- private RecordingSocketFactory(SSLSocketFactory delegate) {
+ public FallbackTestClientSocketFactory(SSLSocketFactory delegate,
+ boolean disableTlsFallbackScsv) {
super(delegate);
+ this.disableTlsFallbackScsv = disableTlsFallbackScsv;
}
- @Override
- public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+ @Override public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
- SSLSocket socket = (SSLSocket) delegate.createSocket(s, host, port, autoClose);
+ SSLSocket socket = super.createSocket(s, host, port, autoClose);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket() throws IOException {
- SSLSocket socket = (SSLSocket) delegate.createSocket();
+ @Override public SSLSocket createSocket() throws IOException {
+ SSLSocket socket = super.createSocket();
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
- SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
+ @Override public SSLSocket createSocket(String host,int port) throws IOException {
+ SSLSocket socket = super.createSocket(host, port);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket(String host, int port, InetAddress localHost,
- int localPort) throws IOException, UnknownHostException {
- SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
+ @Override public SSLSocket createSocket(String host,int port, InetAddress localHost,
+ int localPort) throws IOException {
+ SSLSocket socket = super.createSocket(host, port, localHost, localPort);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket(InetAddress host, int port) throws IOException {
- SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
+ @Override public SSLSocket createSocket(InetAddress host,int port) throws IOException {
+ SSLSocket socket = super.createSocket(host, port);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket(InetAddress address, int port,
+ @Override public SSLSocket createSocket(InetAddress address,int port,
InetAddress localAddress, int localPort) throws IOException {
- SSLSocket socket =
- (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
+ SSLSocket socket = super.createSocket(address, port, localAddress, localPort);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
@@ -2647,4 +3133,31 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
}
+ private static class TlsFallbackDisabledScsvSSLSocket extends DelegatingSSLSocket {
+
+ private boolean tlsFallbackScsvSet;
+
+ public TlsFallbackDisabledScsvSSLSocket(SSLSocket socket) {
+ super(socket);
+ }
+
+ @Override public void setEnabledCipherSuites(String[] suites) {
+ List<String> enabledCipherSuites = new ArrayList<String>(suites.length);
+ for (String suite : suites) {
+ if (suite.equals(FallbackTestClientSocketFactory.TLS_FALLBACK_SCSV)) {
+ // Record that an attempt was made to set TLS_FALLBACK_SCSV, but don't actually
+ // set it.
+ tlsFallbackScsvSet = true;
+ } else {
+ enabledCipherSuites.add(suite);
+ }
+ }
+ delegate.setEnabledCipherSuites(
+ enabledCipherSuites.toArray(new String[enabledCipherSuites.size()]));
+ }
+
+ public boolean wasTlsFallbackScsvSet() {
+ return tlsFallbackScsvSet;
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
index 9789197..41b434d 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
@@ -15,7 +15,6 @@
*/
package libcore.java.nio.channels;
-import android.system.OsConstants;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
@@ -28,7 +27,6 @@ import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
-import libcore.io.Libcore;
import tests.net.StuckServer;
public class SelectorTest extends TestCase {
@@ -71,25 +69,6 @@ public class SelectorTest extends TestCase {
}
}
- // http://b/6453247
- // This test won't work on the host until/unless we start using libcorkscrew there.
- // The runtime itself blocks SIGQUIT, so that doesn't cause poll(2) to EINTR directly.
- // The EINTR is caused by the way libcorkscrew works.
- public void testEINTR() throws Exception {
- Selector selector = Selector.open();
- new Thread(new Runnable() {
- @Override public void run() {
- try {
- Thread.sleep(2000);
- Libcore.os.kill(Libcore.os.getpid(), OsConstants.SIGQUIT);
- } catch (Exception ex) {
- fail();
- }
- }
- }).start();
- assertEquals(0, selector.select());
- }
-
// http://code.google.com/p/android/issues/detail?id=15388
public void testInterrupted() throws IOException {
Selector selector = Selector.open();
diff --git a/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java b/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
index ff510e0..e9ab8ae 100644
--- a/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
+++ b/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
@@ -20,8 +20,10 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class CharsetEncoderTest extends junit.framework.TestCase {
@@ -161,4 +163,71 @@ public class CharsetEncoderTest extends junit.framework.TestCase {
assertEquals(CoderResult.UNDERFLOW, cr);
assertEquals(8, bb.position());
}
+
+ // Discards all input. Outputs a single byte 'X' on flush.
+ private static final class MockCharset extends Charset {
+ static final Charset INSTANCE = new MockCharset();
+
+ private MockCharset() {
+ super("MockCharset", new String[0]);
+ }
+
+ public boolean contains(Charset charset) {
+ return false;
+ }
+
+ public CharsetEncoder newEncoder() {
+ return new CharsetEncoder(INSTANCE, 1.f, 1.f) {
+ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+ in.position(in.limit());
+ return CoderResult.UNDERFLOW;
+ }
+
+ protected CoderResult implFlush(ByteBuffer out) {
+ out.put((byte) 'X');
+ return CoderResult.UNDERFLOW;
+ }
+ };
+ }
+
+ public CharsetDecoder newDecoder() {
+ return new CharsetDecoder(INSTANCE, 1.f, 1.f) {
+ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
+ in.position(in.limit());
+ return CoderResult.UNDERFLOW;
+ }
+ };
+ }
+ }
+
+ // Repeated calls to flush() should not result in repeated calls to implFlush().
+ public void testFlushNotCallingImplFlushRepeatedly() {
+ CharsetEncoder e = MockCharset.INSTANCE.newEncoder();
+ ByteBuffer bb = ByteBuffer.allocate(4);
+ CoderResult cr = e.encode(CharBuffer.allocate(0), bb, true);
+ assertEquals(CoderResult.UNDERFLOW, cr);
+ cr = e.flush(bb);
+ assertEquals(CoderResult.UNDERFLOW, cr);
+ cr = e.flush(bb);
+ assertEquals(CoderResult.UNDERFLOW, cr);
+ assertEquals(1, bb.position());
+ assertEquals((byte) 'X', bb.get(0));
+ assertEquals(0x00, bb.get(1));
+ assertEquals(0x00, bb.get(2));
+ assertEquals(0x00, bb.get(3));
+ }
+
+ // http://b/19185235
+ public void testFlushWithIncompleteInput() {
+ CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
+ ByteBuffer output = ByteBuffer.allocate(10);
+ CoderResult result = encoder.encode(CharBuffer.wrap("\ud800"), output,
+ true /* endOfInput */);
+ assertTrue(result.isUnderflow());
+
+ result = encoder.flush(output);
+ assertTrue(result.isMalformed());
+ assertEquals(1, result.length());
+ assertEquals(0, output.position());
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java b/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java
new file mode 100644
index 0000000..b4886d2
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 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 libcore.java.nio.charset;
+
+import java.nio.charset.Charset;
+import java.nio.charset.spi.CharsetProvider;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * This class is registered as a charset provider by the META-INF in the libcore
+ * tests jar. Since there isn't any convenient API to dynamically register and de-register
+ * charset-providers, this class allows tests to plug in a delegate that lives for the
+ * duration of the test.
+ */
+public final class SettableCharsetProvider extends CharsetProvider {
+ private static CharsetProvider delegate;
+
+ public static void setDelegate(CharsetProvider cp) {
+ delegate = cp;
+ }
+
+ public static void clearDelegate() {
+ delegate = null;
+ }
+
+ @Override
+ public Iterator<Charset> charsets() {
+ if (delegate != null) {
+ return delegate.charsets();
+ }
+
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Charset charsetForName(String charsetName) {
+ if (delegate != null) {
+ return delegate.charsetForName(charsetName);
+ }
+
+ return null;
+ }
+}
diff --git a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
index e7fdb1f..10bb621 100644
--- a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
+++ b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
@@ -45,6 +45,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.crypto.interfaces.DHPrivateKey;
@@ -55,13 +56,32 @@ import junit.framework.TestCase;
public class KeyPairGeneratorTest extends TestCase {
- public void test_providerCount() {
+ private List<Provider> providers = new ArrayList<Provider>();
+
+ @Override
+ public void setUp() {
Provider[] providers = Security.getProviders();
+ for (Provider p : providers) {
+ // Do not test AndroidKeyStore Provider. It does not accept vanilla public keys for
+ // signature verification. It's OKish not to test here because it's tested by
+ // cts/tests/tests/keystore.
+ if (!p.getName().startsWith("AndroidKeyStore")) {
+ this.providers.add(p);
+ }
+ }
+ }
+
+ @Override
+ public void tearDown() {
+ providers.clear();
+ }
+
+ public void test_providerCount() {
// We expect there to be at least one provider.
- assertTrue(providers.length > 0);
+ assertTrue(providers.size() > 0);
// If this fails remember to add _provider methods below. This test is sharded because it
// takes a long time to execute.
- assertTrue(providers.length < 10);
+ assertTrue(providers.size() < 10);
}
public void test_getInstance_provider0() throws Exception {
@@ -105,14 +125,14 @@ public class KeyPairGeneratorTest extends TestCase {
}
private void test_getInstance(int providerIndex) throws Exception {
- Provider[] providers = Security.getProviders();
- if (providerIndex >= providers.length) {
+ if (providerIndex >= providers.size()) {
// Providers can be added by vendors and other tests. We do not
// specify a fixed number and silenty pass if the provider at the
// specified index does not exist.
return;
}
- Provider provider = providers[providerIndex];
+
+ Provider provider = providers.get(providerIndex);
Set<Provider.Service> services = provider.getServices();
for (Provider.Service service : services) {
String type = service.getType();
@@ -120,12 +140,6 @@ public class KeyPairGeneratorTest extends TestCase {
continue;
}
String algorithm = service.getAlgorithm();
-
- // AndroidKeyStore is tested in CTS.
- if ("AndroidKeyStore".equals(provider.getName())) {
- continue;
- }
-
AlgorithmParameterSpec params = null;
if ("DH".equals(algorithm)) {
@@ -195,7 +209,6 @@ public class KeyPairGeneratorTest extends TestCase {
putKeySize("DiffieHellman", 512);
putKeySize("DiffieHellman", 512+64);
putKeySize("DiffieHellman", 1024);
- putKeySize("EC", 192);
putKeySize("EC", 224);
putKeySize("EC", 256);
putKeySize("EC", 384);
@@ -204,10 +217,10 @@ public class KeyPairGeneratorTest extends TestCase {
/** Elliptic Curve Crypto named curves that should be supported. */
private static final String[] EC_NAMED_CURVES = {
- // NIST P-192 aka SECG secp192r1 aka ANSI X9.62 prime192v1
- "secp192r1", "prime192v1",
// NIST P-256 aka SECG secp256r1 aka ANSI X9.62 prime256v1
"secp256r1", "prime256v1",
+ // NIST P-521 aka SECG secp521r1
+ "secp521r1",
};
private void test_KeyPairGenerator(KeyPairGenerator kpg) throws Exception {
@@ -264,7 +277,7 @@ public class KeyPairGeneratorTest extends TestCase {
}
private void test_Key(KeyPairGenerator kpg, Key k) throws Exception {
- String expectedAlgorithm = kpg.getAlgorithm().toUpperCase();
+ String expectedAlgorithm = kpg.getAlgorithm().toUpperCase(Locale.ROOT);
if (StandardNames.IS_RI && expectedAlgorithm.equals("DIFFIEHELLMAN")) {
expectedAlgorithm = "DH";
}
@@ -303,8 +316,6 @@ public class KeyPairGeneratorTest extends TestCase {
byte[] encoded = k.getEncoded();
String keyAlgo = k.getAlgorithm();
-
- Provider[] providers = Security.getProviders();
for (Provider p : providers) {
Set<Provider.Service> services = p.getServices();
for (Provider.Service service : services) {
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index 994214b..d3ccae1 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -41,6 +41,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
@@ -160,7 +161,7 @@ public class ProviderTest extends TestCase {
public void test_Provider_Properties() throws Exception {
/*
* A useful reference on Provider properties
- * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html>
+ * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
* How to Implement a Provider in the Java &trade; Cryptography Architecture
* </a>
*/
@@ -178,8 +179,8 @@ public class ProviderTest extends TestCase {
provider.get("Provider.id className"));
// build map of all known aliases and implementations
- Map<String,String> aliases = new HashMap<String,String>();
- Map<String,String> implementations = new HashMap<String,String>();
+ Map<String,String> aliases = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ Map<String,String> implementations = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (Entry<Object,Object> entry : provider.entrySet()) {
Object k = entry.getKey();
Object v = entry.getValue();
@@ -218,7 +219,8 @@ public class ProviderTest extends TestCase {
} catch (ClassNotFoundException e) {
// Sun forgot their own class
if (!className.equals("sun.security.pkcs11.P11MAC")) {
- fail("Could not find class " + className + " for " + typeAndAlgorithm);
+ fail("Could not find class " + className + " for " + typeAndAlgorithm
+ + " [provider=" + provider.getName() + "]");
}
}
}
@@ -227,8 +229,9 @@ public class ProviderTest extends TestCase {
for (Entry<String,String> entry : aliases.entrySet()) {
String alias = entry.getKey();
String actual = entry.getValue();
- assertTrue("Could not find implementation " + actual + " for alias " + alias,
- implementations.containsKey(actual));
+ assertTrue("Could not find implementation " + actual + " for alias " + alias +
+ " [provider=" + provider.getName() + "]",
+ implementations.containsKey(actual));
}
}
}
@@ -547,6 +550,15 @@ public class ProviderTest extends TestCase {
}
}
+ public void testProvider_removeProvider_Success() throws Exception {
+ MockProvider provider = new MockProvider("MockProvider");
+ assertNull(Security.getProvider(provider.getName()));
+ Security.addProvider(provider);
+ assertNotNull(Security.getProvider(provider.getName()));
+ Security.removeProvider(provider.getName());
+ assertNull(Security.getProvider(provider.getName()));
+ }
+
public static class MyCertStoreSpi extends CertStoreSpi {
public MyCertStoreSpi(CertStoreParameters params) throws InvalidAlgorithmParameterException {
super(params);
diff --git a/luni/src/test/java/libcore/java/security/SecureRandomTest.java b/luni/src/test/java/libcore/java/security/SecureRandomTest.java
index f9edbaa..e296775 100644
--- a/luni/src/test/java/libcore/java/security/SecureRandomTest.java
+++ b/luni/src/test/java/libcore/java/security/SecureRandomTest.java
@@ -102,6 +102,7 @@ public class SecureRandomTest extends TestCase {
public void testNewConstructors_Success() throws Exception {
SecureRandom sr1 = new SecureRandom();
+ assertNotNull(sr1.getProvider());
assertEquals(EXPECTED_PROVIDER, sr1.getProvider().getClass().getName());
test_SecureRandom(sr1);
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index 5e02f10..9aa75bd 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -31,6 +31,11 @@ import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPrivateKeySpec;
@@ -71,6 +76,49 @@ public class SignatureTest extends TestCase {
}
}
+ public void testSignature_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+ put("Signature.FOO SupportedKeyClasses", "None");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Signature s = Signature.getInstance("FOO", mockProvider);
+ s.initSign(new MockPrivateKey());
+ assertEquals(mockProvider, s.getProvider());
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
+ /**
+ * Several exceptions can be thrown by init. Check that in this case we throw the right one,
+ * as the error could fall under the umbrella of other exceptions.
+ * http://b/18987633
+ */
+ public void testSignature_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+ put("Signature.FOO SupportedKeyClasses", "None");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Signature s = Signature.getInstance("FOO");
+ s.initSign(new MockPrivateKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
public void testSignature_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
@@ -238,6 +286,12 @@ public class SignatureTest extends TestCase {
public void test_getInstance() throws Exception {
Provider[] providers = Security.getProviders();
for (Provider provider : providers) {
+ // Do not test AndroidKeyStore's Signature. It needs an AndroidKeyStore-specific key.
+ // It's OKish not to test AndroidKeyStore's Signature here because it's tested
+ // by cts/tests/test/keystore.
+ if (provider.getName().startsWith("AndroidKeyStore")) {
+ continue;
+ }
Set<Provider.Service> services = provider.getServices();
for (Provider.Service service : services) {
String type = service.getType();
@@ -370,9 +424,14 @@ public class SignatureTest extends TestCase {
return data;
}
- // http://code.google.com/p/android/issues/detail?id=18566
- // http://b/5038554
- public void test18566() throws Exception {
+ /**
+ * This should actually fail because the ASN.1 encoding is incorrect. It is
+ * missing the NULL in the AlgorithmIdentifier field.
+ * <p>
+ * http://code.google.com/p/android/issues/detail?id=18566 <br/>
+ * http://b/5038554
+ */
+ public void test18566_AlgorithmOid_MissingNull_Failure() throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(PK_BYTES);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pk = keyFactory.generatePublic(keySpec);
@@ -380,7 +439,7 @@ public class SignatureTest extends TestCase {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pk);
sig.update(CONTENT);
- assertTrue(sig.verify(SIGNATURE));
+ assertFalse(sig.verify(SIGNATURE));
}
/*
@@ -1646,4 +1705,43 @@ public class SignatureTest extends TestCase {
es.shutdown();
assertTrue("Test should not timeout", es.awaitTermination(1, TimeUnit.MINUTES));
}
+
+ public void testArbitraryCurve() throws Exception {
+ // These are the parameters for the BitCoin curve (secp256k1). See
+ // https://en.bitcoin.it/wiki/Secp256k1.
+ final BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
+ final BigInteger a = BigInteger.valueOf(0);
+ final BigInteger b = BigInteger.valueOf(7);
+ final BigInteger x = new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16);
+ final BigInteger y = new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16);
+ final BigInteger order = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
+ final int cofactor = 1;
+
+ final ECParameterSpec spec = new ECParameterSpec(new EllipticCurve(new ECFieldFp(p), a, b), new ECPoint(x, y), order, cofactor);
+ final KeyFactory factory = KeyFactory.getInstance("EC");
+
+ // $ openssl ecparam -name secp256k1 -genkey > key.pem
+ // $ openssl ec -text -noout < key.pem
+ final BigInteger Px = new BigInteger("2d45572747a625db5fd23b30f97044a682f2d42d31959295043c1fa0034c8ed3", 16);
+ final BigInteger Py = new BigInteger("4d330f52e4bba00145a331041c8bbcf300c4fbfdf3d63d8de7608155b2793808", 16);
+
+ final ECPublicKeySpec keySpec = new ECPublicKeySpec(new ECPoint(Px, Py), spec);
+ final PublicKey pub = factory.generatePublic(keySpec);
+
+ // $ echo -n "Satoshi Nakamoto" > signed
+ // $ openssl dgst -ecdsa-with-SHA1 -sign key.pem -out sig signed
+ final byte[] SIGNATURE = hexToBytes("304402205b41ece6dcc1c5bfcfdae74658d99c08c5e783f3926c11ecc1a8bea5d95cdf27022061a7d5fc687287e2e02dd7c6723e2e27fe0555f789590a37e96b1bb0355b4df0");
+
+ Signature ecdsaVerify = Signature.getInstance("SHA1withECDSA");
+ ecdsaVerify.initVerify(pub);
+ ecdsaVerify.update("Satoshi Nakamoto".getBytes("UTF-8"));
+ boolean result = ecdsaVerify.verify(SIGNATURE);
+ assertEquals(true, result);
+
+ ecdsaVerify = Signature.getInstance("SHA1withECDSA");
+ ecdsaVerify.initVerify(pub);
+ ecdsaVerify.update("Not Satoshi Nakamoto".getBytes("UTF-8"));
+ result = ecdsaVerify.verify(SIGNATURE);
+ assertEquals(false, result);
+ }
}
diff --git a/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java b/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
index e2f21e8..a3a721a 100644
--- a/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
@@ -16,11 +16,13 @@
package libcore.java.security.cert;
+import com.android.org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
-import com.android.org.bouncycastle.asn1.x509.X509Extensions;
+import com.android.org.bouncycastle.asn1.x509.Extension;
+import com.android.org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
import com.android.org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
-import com.android.org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -28,13 +30,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
-import java.io.OptionalDataException;
-import java.io.StreamCorruptedException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
+import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
@@ -554,25 +557,26 @@ public class CertificateFactoryTest extends TestCase {
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+ PublicKey pubKey = keyPair.getPublic();
certGen.setSerialNumber(serial);
certGen.setIssuerDN(issuerPrincipal);
certGen.setNotBefore(startDate);
certGen.setNotAfter(expiryDate);
certGen.setSubjectDN(subjectPrincipal);
- certGen.setPublicKey(keyPair.getPublic());
+ certGen.setPublicKey(pubKey);
certGen.setSignatureAlgorithm("SHA1withRSA");
if (issuer != null) {
- certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
+ certGen.addExtension(Extension.authorityKeyIdentifier, false,
new AuthorityKeyIdentifierStructure(issuer.certificate));
} else {
- certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
- new AuthorityKeyIdentifierStructure(keyPair.getPublic()));
+ certGen.addExtension(Extension.authorityKeyIdentifier, false,
+ new AuthorityKeyIdentifier(generatePublicKeyDigest(pubKey)));
}
- certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
- new SubjectKeyIdentifierStructure(keyPair.getPublic()));
- certGen.addExtension(X509Extensions.BasicConstraints, true, basicConstraints);
+ certGen.addExtension(Extension.subjectKeyIdentifier, false,
+ new SubjectKeyIdentifier(generatePublicKeyDigest(pubKey)));
+ certGen.addExtension(Extension.basicConstraints, true, basicConstraints);
X509Certificate cert = certGen.generate(caKey);
@@ -582,4 +586,18 @@ public class CertificateFactoryTest extends TestCase {
return holder;
}
+
+ /**
+ * Generates a type 1 key identifier according to RFC 3280 4.2.1.2.
+ */
+ private static byte[] generatePublicKeyDigest(PublicKey pubKey) {
+ SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded());
+ MessageDigest sha1digest;
+ try {
+ sha1digest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("SHA-1 not available");
+ }
+ return sha1digest.digest(spki.getPublicKeyData().getBytes());
+ }
}
diff --git a/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java b/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
index 42de50a..1611120 100644
--- a/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
@@ -256,15 +256,7 @@ public class X509CRLTest extends TestCase {
private void getSigAlgName(CertificateFactory f) throws Exception {
X509CRL crlRsa = getCRL(f, CRL_RSA);
-
- String actual = crlRsa.getSigAlgName().toUpperCase(Locale.US);
-
- // Bouncycastle is broken
- if ("BC".equals(f.getProvider().getName())) {
- assertEquals("1.2.840.113549.1.1.5", actual);
- } else {
- assertEquals("SHA1WITHRSA", actual);
- }
+ assertEquals("SHA1WITHRSA", getCRL(f, CRL_RSA).getSigAlgName().toUpperCase(Locale.ROOT));
}
private void getSigAlgOID(CertificateFactory f) throws Exception {
diff --git a/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java b/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java
index c35f8e6..14c22ef 100644
--- a/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/X509CertificateTest.java
@@ -339,6 +339,13 @@ public class X509CertificateTest extends TestCase {
Provider[] providers = Security.getProviders("Signature." + c.getSigAlgName());
for (Provider p : providers) {
+ // Do not test AndroidKeyStore Provider. It does not accept vanilla public keys for
+ // signature verification. It's OKish not to test here because it's tested by
+ // cts/tests/tests/keystore.
+ if (p.getName().startsWith("AndroidKeyStore")) {
+ continue;
+ }
+
c.verify(signer, p.getName());
try {
diff --git a/luni/src/test/java/libcore/java/sql/TimestampTest.java b/luni/src/test/java/libcore/java/sql/TimestampTest.java
index 2985848..71ac8c8 100644
--- a/luni/src/test/java/libcore/java/sql/TimestampTest.java
+++ b/luni/src/test/java/libcore/java/sql/TimestampTest.java
@@ -144,4 +144,12 @@ public final class TimestampTest extends TestCase {
} catch (IllegalArgumentException expected) { }
}
+ // http://b/19756610
+ public void testAsymmetricEquals() {
+ Timestamp timestamp = new Timestamp(0);
+ java.util.Date date = new java.util.Date(0);
+
+ assertTrue(date.equals(timestamp));
+ assertFalse(timestamp.equals(date));
+ }
}
diff --git a/luni/src/test/java/libcore/java/text/BreakIteratorTest.java b/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
index 47701c8..de2ae52 100644
--- a/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
+++ b/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
@@ -172,32 +172,4 @@ public class BreakIteratorTest extends junit.framework.TestCase {
// Expected exception
}
}
-
- // http://code.google.com/p/android/issues/detail?id=41143
- // This code is inherently unsafe and crazy;
- // we're just trying to provoke native crashes!
- public void testConcurrentBreakIteratorAccess() throws Exception {
- final BreakIterator it = BreakIterator.getCharacterInstance();
-
- ArrayList<Thread> threads = new ArrayList<Thread>();
- for (int i = 0; i < 10; ++i) {
- Thread t = new Thread(new Runnable() {
- public void run() {
- for (int i = 0; i < 4096; ++i) {
- it.setText("some example text");
- for (int index = it.first(); index != BreakIterator.DONE; index = it.next()) {
- }
- }
- }
- });
- threads.add(t);
- }
-
- for (Thread t : threads) {
- t.start();
- }
- for (Thread t : threads) {
- t.join();
- }
- }
}
diff --git a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
index e6933e6..0c97f34 100644
--- a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
+++ b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
@@ -147,17 +147,17 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase {
}
// http://b/7955614
- public void test_getZoneStrings_GMT_short_names() throws Exception {
+ public void test_getZoneStrings_Apia() throws Exception {
String[][] array = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
for (int i = 0; i < array.length; ++i) {
String[] row = array[i];
- // America/Santiago is somewhat arbitrary; we just want a zone we have to generate
+ // Pacific/Apia is somewhat arbitrary; we just want a zone we have to generate
// "GMT" strings for the short names.
- if (row[0].equals("America/Santiago")) {
- assertEquals("Chile Standard Time", row[1]);
- assertEquals("GMT-03:00", row[2]);
- assertEquals("Chile Summer Time", row[3]);
- assertEquals("GMT-03:00", row[4]);
+ if (row[0].equals("Pacific/Apia")) {
+ assertEquals("Apia Standard Time", row[1]);
+ assertEquals("GMT+13:00", row[2]);
+ assertEquals("Apia Daylight Time", row[3]);
+ assertEquals("GMT+14:00", row[4]);
}
}
}
diff --git a/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java
index 3e0aeba..b1d37f3 100644
--- a/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java
+++ b/luni/src/test/java/libcore/java/text/DecimalFormatSymbolsTest.java
@@ -86,4 +86,34 @@ public class DecimalFormatSymbolsTest extends junit.framework.TestCase {
assertEquals("$", dfs.getCurrencySymbol());
assertEquals(null, dfs.getInternationalCurrencySymbol());
}
+
+ // https://code.google.com/p/android/issues/detail?id=170718
+ public void testSerializationOfMultiCharNegativeAndPercentage() throws Exception {
+ DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.forLanguageTag("ar-AR"));
+ assertTrue(dfs.getMinusSignString().length() > 1);
+ assertTrue(dfs.getPercentString().length() > 1);
+
+ // Serialize...
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ new ObjectOutputStream(out).writeObject(dfs);
+ byte[] bytes = out.toByteArray();
+
+ // Deserialize...
+ ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
+ DecimalFormatSymbols deserializedDfs = (DecimalFormatSymbols) in.readObject();
+ assertEquals(-1, in.read());
+
+ assertEquals(dfs.getMinusSignString(), deserializedDfs.getMinusSignString());
+ assertEquals(dfs.getPercentString(), deserializedDfs.getPercentString());
+ }
+
+ // http://b/18785260
+ public void testMultiCharMinusSignAndPercentage() {
+ DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.forLanguageTag("ar-AR"));
+ assertTrue(dfs.getMinusSignString().length() > 1);
+ assertTrue(dfs.getPercentString().length() > 1);
+
+ assertEquals('%', dfs.getPercent());
+ assertEquals('-', dfs.getMinusSign());
+ }
}
diff --git a/luni/src/test/java/libcore/java/text/NumberFormatTest.java b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
index 0678e96..87fe96d 100644
--- a/luni/src/test/java/libcore/java/text/NumberFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
@@ -17,6 +17,7 @@
package libcore.java.text;
import java.math.BigInteger;
+import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.FieldPosition;
@@ -93,6 +94,21 @@ public class NumberFormatTest extends junit.framework.TestCase {
}
}
+ public void testPercentageRounding() throws Exception {
+ NumberFormat nf = NumberFormat.getPercentInstance(Locale.US);
+ assertEquals("15%", nf.format(0.149));
+ assertEquals("14%", nf.format(0.142));
+
+ nf.setRoundingMode(RoundingMode.UP);
+ assertEquals("15%", nf.format(0.142));
+
+ nf.setRoundingMode(RoundingMode.DOWN);
+ assertEquals("14%", nf.format(0.149));
+
+ nf.setMaximumFractionDigits(1);
+ assertEquals("14.9%", nf.format(0.149));
+ }
+
// https://code.google.com/p/android/issues/detail?id=62269
public void test_62269() throws Exception {
NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
diff --git a/luni/src/test/java/libcore/java/util/CalendarTest.java b/luni/src/test/java/libcore/java/util/CalendarTest.java
index e0e1a35..2e13ad8 100644
--- a/luni/src/test/java/libcore/java/util/CalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/CalendarTest.java
@@ -263,4 +263,76 @@ public class CalendarTest extends junit.framework.TestCase {
b.setTime(d);
assertEquals(a, b);
}
+
+ public void testCloneMakesDeepCopyOfCalendarFields() {
+ FakeCalendar c = new FakeCalendar();
+ FakeCalendar c2 = (FakeCalendar) c.clone();
+
+ assertFalse(c.getTimeZone() == c2.getTimeZone());
+ assertEquals(c.getTimeZone(), c2.getTimeZone());
+
+ // The default clone() implementation makes a deep copy of calendar
+ // fields...
+ assertFalse(c.getCalenderFields() == c2.getCalenderFields());
+ // ,,, and a shallow copy of subclass fields.
+ assertSame(c.getSubclassFields(), c2.getSubclassFields());
+ }
+
+ public static class FakeCalendar extends Calendar {
+
+ private int[] subclassFields;
+
+ public FakeCalendar() {
+ super(TimeZone.getDefault(), Locale.getDefault());
+ subclassFields = new int[12];
+ }
+
+ public int[] getCalenderFields() {
+ return fields;
+ }
+
+ public int[] getSubclassFields() {
+ return subclassFields;
+ }
+
+ @Override
+ public void add(int field, int value) {
+
+ }
+
+ @Override
+ protected void computeFields() {
+
+ }
+
+ @Override
+ protected void computeTime() {
+
+ }
+
+ @Override
+ public int getGreatestMinimum(int field) {
+ return 0;
+ }
+
+ @Override
+ public int getLeastMaximum(int field) {
+ return 0;
+ }
+
+ @Override
+ public int getMaximum(int field) {
+ return 0;
+ }
+
+ @Override
+ public int getMinimum(int field) {
+ return 0;
+ }
+
+ @Override
+ public void roll(int field, boolean increment) {
+
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/CollectionsTest.java b/luni/src/test/java/libcore/java/util/CollectionsTest.java
index 80c769e..bc73817 100644
--- a/luni/src/test/java/libcore/java/util/CollectionsTest.java
+++ b/luni/src/test/java/libcore/java/util/CollectionsTest.java
@@ -17,7 +17,10 @@
package libcore.java.util;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.ListIterator;
@@ -94,4 +97,92 @@ public final class CollectionsTest extends TestCase {
} catch (IllegalStateException expected) {
}
}
+
+ public void testSortFastPath_incrementsModcount() {
+ ArrayList<String> list = new ArrayList<String>(16);
+ list.add("coven");
+ list.add("asylum");
+ list.add("murder house");
+ list.add("freak show");
+
+ Iterator<String> it = list.iterator();
+ it.next();
+ Collections.sort(list);
+ try {
+ it.next();
+ fail();
+ } catch (ConcurrentModificationException expected) {
+ }
+ }
+
+ /**
+ * A value type whose {@code compareTo} method returns one of {@code 0},
+ * {@code Integer.MIN_VALUE} and {@code Integer.MAX_VALUE}.
+ */
+ static final class IntegerWithExtremeComparator
+ implements Comparable<IntegerWithExtremeComparator> {
+ private final int value;
+
+ public IntegerWithExtremeComparator(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public int compareTo(IntegerWithExtremeComparator another) {
+ if (another.value == this.value) {
+ return 0;
+ } else if (another.value > this.value) {
+ return Integer.MIN_VALUE;
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+ }
+
+ // http://b/19749094
+ public void testBinarySearch_comparatorThatReturnsMinAndMaxValue() {
+ ArrayList<Integer> list = new ArrayList<Integer>(16);
+ list.add(4);
+ list.add(9);
+ list.add(11);
+ list.add(14);
+ list.add(16);
+
+ int index = Collections.binarySearch(list, 9, new Comparator<Integer>() {
+ @Override
+ public int compare(Integer lhs, Integer rhs) {
+ final int compare = lhs.compareTo(rhs);
+ if (compare == 0) {
+ return 0;
+ } else if (compare < 0) {
+ return Integer.MIN_VALUE;
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+ });
+ assertEquals(1, index);
+
+ ArrayList<IntegerWithExtremeComparator> list2 =
+ new ArrayList<IntegerWithExtremeComparator>();
+ list2.add(new IntegerWithExtremeComparator(4));
+ list2.add(new IntegerWithExtremeComparator(9));
+ list2.add(new IntegerWithExtremeComparator(11));
+ list2.add(new IntegerWithExtremeComparator(14));
+ list2.add(new IntegerWithExtremeComparator(16));
+
+ assertEquals(1, Collections.binarySearch(list2, new IntegerWithExtremeComparator(9)));
+ }
+
+ public void testBinarySearch_emptyCollection() {
+ assertEquals(-1, Collections.binarySearch(new ArrayList<Integer>(), 9));
+
+ assertEquals(-1, Collections.binarySearch(new ArrayList<Integer>(), 9,
+ new Comparator<Integer>() {
+ @Override
+ public int compare(Integer lhs, Integer rhs) {
+ return lhs.compareTo(rhs);
+ }
+ }));
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/CurrencyTest.java b/luni/src/test/java/libcore/java/util/CurrencyTest.java
index cf2a1b6..e4c56e5 100644
--- a/luni/src/test/java/libcore/java/util/CurrencyTest.java
+++ b/luni/src/test/java/libcore/java/util/CurrencyTest.java
@@ -58,7 +58,7 @@ public class CurrencyTest extends junit.framework.TestCase {
assertEquals("Swiss Franc", Currency.getInstance("CHF").getDisplayName(Locale.US));
assertEquals("Schweizer Franken", Currency.getInstance("CHF").getDisplayName(new Locale("de", "CH")));
assertEquals("franc suisse", Currency.getInstance("CHF").getDisplayName(new Locale("fr", "CH")));
- assertEquals("Franco Svizzero", Currency.getInstance("CHF").getDisplayName(new Locale("it", "CH")));
+ assertEquals("franco svizzero", Currency.getInstance("CHF").getDisplayName(new Locale("it", "CH")));
}
public void test_getDefaultFractionDigits() throws Exception {
diff --git a/luni/src/test/java/libcore/java/util/DateTest.java b/luni/src/test/java/libcore/java/util/DateTest.java
index 3ed0952..076c6e2 100644
--- a/luni/src/test/java/libcore/java/util/DateTest.java
+++ b/luni/src/test/java/libcore/java/util/DateTest.java
@@ -31,12 +31,13 @@ public class DateTest extends TestCase {
assertEquals("Wed Dec 31 18:00:00 CST 1969", new Date(0).toString());
}
+ // https://code.google.com/p/android/issues/detail?id=81924
public void test_toString_nonUs() {
// The string for the timezone depends on what the default locale is. Not every locale
- // has a short-name for America/Chicago -> PST.
- Locale.setDefault(Locale.UK);
+ // has a short-name for America/Chicago -> CST.
+ Locale.setDefault(Locale.CHINA);
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
- assertEquals("Wed Dec 31 18:00:00 GMT-06:00 1969", new Date(0).toString());
+ assertEquals("Wed Dec 31 18:00:00 CST 1969", new Date(0).toString());
}
public void test_toGMTString_us() throws Exception {
@@ -61,10 +62,10 @@ public class DateTest extends TestCase {
Calendar c = Calendar.getInstance();
c.clear();
c.set(Calendar.YEAR, 21);
- assertEquals("Wed Jan 01 00:00:00 GMT-08:00 21", c.getTime().toString());
+ assertEquals("Wed Jan 01 00:00:00 PST 21", c.getTime().toString());
assertEquals("1 Jan 21 08:00:00 GMT", c.getTime().toGMTString());
c.set(Calendar.YEAR, 321);
- assertEquals("Sun Jan 01 00:00:00 GMT-08:00 321", c.getTime().toString());
+ assertEquals("Sun Jan 01 00:00:00 PST 321", c.getTime().toString());
assertEquals("1 Jan 321 08:00:00 GMT", c.getTime().toGMTString());
}
}
diff --git a/luni/src/test/java/libcore/java/util/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java
index c72ecd7..9005f25 100644
--- a/luni/src/test/java/libcore/java/util/LocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/LocaleTest.java
@@ -20,6 +20,7 @@ import java.text.BreakIterator;
import java.text.Collator;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
+import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.IllformedLocaleException;
@@ -1146,4 +1147,94 @@ public class LocaleTest extends junit.framework.TestCase {
assertEquals("variant", locale.getVariant());
assertEquals(locale, Locale.forLanguageTag(locale.toLanguageTag()));
}
+
+ public void testArabicDigits() throws Exception {
+ // ar-DZ uses latn digits by default, but we can override that.
+ Locale ar_DZ = Locale.forLanguageTag("ar-DZ");
+ Locale ar_DZ_arab = Locale.forLanguageTag("ar-DZ-u-nu-arab");
+ Locale ar_DZ_latn = Locale.forLanguageTag("ar-DZ-u-nu-latn");
+ assertEquals('0', new DecimalFormatSymbols(ar_DZ).getZeroDigit());
+ assertEquals('\u0660', new DecimalFormatSymbols(ar_DZ_arab).getZeroDigit());
+ assertEquals('0', new DecimalFormatSymbols(ar_DZ_latn).getZeroDigit());
+
+ // ar-EG uses arab digits by default, but we can override that.
+ Locale ar_EG = Locale.forLanguageTag("ar-EG");
+ Locale ar_EG_arab = Locale.forLanguageTag("ar-EG-u-nu-arab");
+ Locale ar_EG_latn = Locale.forLanguageTag("ar-EG-u-nu-latn");
+ assertEquals('\u0660', new DecimalFormatSymbols(ar_EG).getZeroDigit());
+ assertEquals('\u0660', new DecimalFormatSymbols(ar_EG_arab).getZeroDigit());
+ assertEquals('0', new DecimalFormatSymbols(ar_EG_latn).getZeroDigit());
+ }
+
+ public void testDefaultLocale() throws Exception {
+ final String userLanguage = System.getProperty("user.language", "");
+ final String userRegion = System.getProperty("user.region", "");
+ final String userLocale = System.getProperty("user.locale", "");
+ try {
+ // Assert that user.locale gets priority.
+ System.setUnchangeableSystemProperty("user.locale", "de-DE");
+ System.setUnchangeableSystemProperty("user.language", "en");
+ System.setUnchangeableSystemProperty("user.region", "US");
+
+ Locale l = Locale.getDefaultLocaleFromSystemProperties();
+ assertEquals("de", l.getLanguage());
+ assertEquals("DE", l.getCountry());
+
+ // Assert that it's parsed as a full language tag.
+ System.setUnchangeableSystemProperty("user.locale", "de-Latn-DE");
+ System.setUnchangeableSystemProperty("user.language", "en");
+ System.setUnchangeableSystemProperty("user.region", "US");
+
+ l = Locale.getDefaultLocaleFromSystemProperties();
+ assertEquals("de", l.getLanguage());
+ assertEquals("DE", l.getCountry());
+ assertEquals("Latn", l.getScript());
+
+ // Assert that we use "und" if we're faced with a bad language tag, and
+ // that we don't end up with a null default locale or an exception.
+ System.setUnchangeableSystemProperty("user.locale", "dexx-Latn-DE");
+
+ l = Locale.getDefaultLocaleFromSystemProperties();
+ assertEquals("und", l.getLanguage());
+ assertEquals("DE", l.getCountry());
+ } finally {
+ System.setUnchangeableSystemProperty("user.language", userLanguage);
+ System.setUnchangeableSystemProperty("user.region", userRegion);
+ System.setUnchangeableSystemProperty("user.locale", userLocale);
+ }
+ }
+
+ // http://b/20252611
+ public void testLegacyLocalesWithExtensions() {
+ Locale ja_JP_JP = new Locale("ja", "JP", "JP");
+ assertEquals("ca-japanese", ja_JP_JP.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
+ assertEquals("japanese", ja_JP_JP.getUnicodeLocaleType("ca"));
+
+ Locale th_TH_TH = new Locale("th", "TH", "TH");
+ assertEquals("nu-thai", th_TH_TH.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
+ assertEquals("thai", th_TH_TH.getUnicodeLocaleType("nu"));
+ }
+
+ // http://b/20252611
+ public void testLowerCaseExtensionKeys() {
+ // We must lowercase extension keys in forLanguageTag..
+ Locale ar_EG = Locale.forLanguageTag("ar-EG-U-nu-arab");
+ assertEquals("nu-arab", ar_EG.getExtension(Locale.UNICODE_LOCALE_EXTENSION));
+ assertEquals("ar-EG-u-nu-arab", ar_EG.toLanguageTag());
+
+ // ... and in builders.
+ Locale.Builder b = new Locale.Builder();
+ b.setLanguage("ar");
+ b.setRegion("EG");
+ b.setExtension('U', "nu-arab");
+ assertEquals("ar-EG-u-nu-arab", b.build().toLanguageTag());
+
+ // Corollary : extension keys are case insensitive.
+ b = new Locale.Builder();
+ b.setLanguage("ar");
+ b.setRegion("EG");
+ b.setExtension('U', "nu-arab");
+ b.setExtension('u', "nu-thai");
+ assertEquals("ar-EG-u-nu-thai", b.build().toLanguageTag());
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
index ecf2e5f..7a5fc4a 100644
--- a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
@@ -108,7 +108,7 @@ public class OldTimeZoneTest extends TestCase {
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
assertEquals("Pacific Daylight Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
assertEquals("Pacific Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.UK));
- assertEquals("heure avanc\u00e9e du Pacifique",
+ assertEquals("heure d’été du Pacifique",
tz.getDisplayName(true, TimeZone.LONG, Locale.FRANCE));
assertEquals("heure normale du Pacifique nord-américain",
tz.getDisplayName(false, TimeZone.LONG, Locale.FRANCE));
@@ -123,18 +123,6 @@ public class OldTimeZoneTest extends TestCase {
assertEquals("GMT-07:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.FRANCE));
assertEquals("GMT-08:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
assertEquals("GMT-07:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
-
- // The RI behavior mentioned above does not appear to be because "PST" is a legacy
- // three-character timezone supported by the RI: it happens for "Asia/Tehran"/"IRST" too
- // (IRST is not a legacy code). The RI may just use a different dataset that has "PST" /
- // "IRST" as valid translations (even for scripts like Chinese).
- TimeZone iranTz = TimeZone.getTimeZone("Asia/Tehran");
- assertEquals("Iran Summer Time", iranTz.getDisplayName(true, TimeZone.LONG, Locale.UK));
- assertEquals("Iran Daylight Time", iranTz.getDisplayName(true, TimeZone.LONG, Locale.US));
- assertEquals("Iran Standard Time", iranTz.getDisplayName(false, TimeZone.LONG, Locale.UK));
- assertEquals("Iran Standard Time", iranTz.getDisplayName(false, TimeZone.LONG, Locale.US));
- assertEquals("GMT+03:30", iranTz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
- assertEquals("GMT+04:30", iranTz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
}
public void test_getID() {
diff --git a/luni/src/test/java/libcore/java/util/TimSortTest.java b/luni/src/test/java/libcore/java/util/TimSortTest.java
new file mode 100644
index 0000000..0e928ba
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/TimSortTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 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 libcore.java.util;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * This test is based on test data generated by
+ * https://github.com/abstools/java-timsort-bug/blob/master/TestTimSort.java
+ */
+public class TimSortTest extends TestCase {
+
+ private static final Comparator<Integer> NATURAL_ORDER_COMPARATOR = new Comparator<Integer>() {
+ public int compare(Integer first, Integer second) {
+ return first.compareTo(second);
+ }
+ };
+
+ private static final int BAD_DATA_SIZE = 65536;
+
+ private static int[] BAD_RUN_OFFSETS = {
+ 20204, 20221, 20237, 20255, 20289, 20363, 20521, 20837, 21469, 22733, 25260, 30315,
+ 40408, 40425, 40441, 40459, 40493, 40567, 40725, 41041, 41673, 42936, 45463, 50500,
+ 50517, 50533, 50551, 50585, 50659, 50817, 51133, 51764, 53027, 55536, 55553, 55569,
+ 55587, 55621, 55695, 55853, 56168, 56799, 58044, 58061, 58077, 58095, 58129, 58203,
+ 58360, 58675, 59288, 59305, 59321, 59339, 59373, 59446, 59603, 59900, 59917, 59933,
+ 59951, 59985, 60059, 60196, 60217, 60236, 60274, 60332, 60351, 60369, 60389, 60405,
+ };
+
+ public void testBug19493779WithComparable() throws Exception {
+ Integer[] array = createBugTriggerData();
+ Arrays.sort(array);
+ // The bug caused an ArrayIndexOutOfBoundsException, but we check this anyway.
+ assertSorted(array);
+ }
+
+ public void testBug19493779WithComparator() throws Exception {
+ Integer[] array = createBugTriggerData();
+ Arrays.sort(array, NATURAL_ORDER_COMPARATOR);
+ // The bug caused an ArrayIndexOutOfBoundsException, but we check this anyway.
+ assertSorted(array);
+ }
+
+ private static void assertSorted(Integer[] arrayToSort) {
+ for (int i = 1; i < arrayToSort.length; i++) {
+ if (arrayToSort[i - 1] > arrayToSort[i]) {
+ fail("Array not sorted at element " + i + ": " + Arrays.toString(arrayToSort));
+ }
+ }
+ }
+
+ private static Integer[] createBugTriggerData() {
+ final Integer zero = 0;
+ final Integer one = 1;
+
+ Integer[] bugTriggerData = new Integer[BAD_DATA_SIZE];
+ for (int i = 0; i < bugTriggerData.length; i++) {
+ bugTriggerData[i] = zero;
+ }
+
+ for (int i = 0; i < BAD_RUN_OFFSETS.length; i++) {
+ bugTriggerData[BAD_RUN_OFFSETS[i]] = one;
+ }
+ return bugTriggerData;
+ }
+} \ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 1ca950c..68e9109 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -73,6 +73,12 @@ public class TimeZoneTest extends TestCase {
assertFalse(tz.inDaylightTime(date));
}
+ public void testGetDisplayNameShort_nonHourOffsets() {
+ TimeZone iranTz = TimeZone.getTimeZone("Asia/Tehran");
+ assertEquals("GMT+03:30", iranTz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
+ assertEquals("GMT+04:30", iranTz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
+ }
+
public void testPreHistoricOffsets() throws Exception {
// "Africa/Bissau" has just a few transitions and hasn't changed in a long time.
// 1912-01-01 00:02:19-0100 ... 1912-01-01 00:02:20-0100
@@ -256,12 +262,12 @@ public class TimeZoneTest extends TestCase {
}
// http://b/7955614
- public void test_getDisplayName_GMT_short_names() throws Exception {
- TimeZone tz = TimeZone.getTimeZone("America/Santiago");
- assertEquals("Chile Summer Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
- assertEquals("Chile Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.US));
- assertEquals("GMT-03:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.US));
- assertEquals("GMT-03:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.US));
+ public void testApia() throws Exception {
+ TimeZone tz = TimeZone.getTimeZone("Pacific/Apia");
+ assertEquals("Apia Daylight Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
+ assertEquals("Apia Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.US));
+ assertEquals("GMT+14:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.US));
+ assertEquals("GMT+13:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.US));
}
private static boolean isGmtString(String s) {
@@ -290,4 +296,32 @@ public class TimeZoneTest extends TestCase {
}
}
}
+
+ // http://b/18839557
+ public void testOverflowing32BitUnixDates() {
+ final TimeZone tz = TimeZone.getTimeZone("America/New_York");
+
+ // This timezone didn't have any daylight savings prior to 1917 and this
+ // date is sometime in 1901.
+ assertFalse(tz.inDaylightTime(new Date(-2206292400000L)));
+ assertEquals(-18000000, tz.getOffset(-2206292400000L));
+
+ // Nov 30th 2039, no daylight savings as per current rules.
+ assertFalse(tz.inDaylightTime(new Date(2206292400000L)));
+ assertEquals(-18000000, tz.getOffset(2206292400000L));
+ }
+
+ public void testTimeZoneIDLocalization() {
+ Locale defaultLocale = Locale.getDefault();
+ try {
+ Locale.setDefault(new Locale("en"));
+ TimeZone en_timezone = TimeZone.getTimeZone("GMT+09:00");
+ Locale.setDefault(new Locale("ar"));
+ TimeZone ar_timezone = TimeZone.getTimeZone("GMT+09:00");
+
+ assertEquals(en_timezone.getID(), ar_timezone.getID());
+ } finally {
+ Locale.setDefault(defaultLocale);
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
index e5a6cd8..9496ad0 100644
--- a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
+++ b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
@@ -169,6 +169,10 @@ public class StrictJarFileTest extends TestCase {
assertThrowsOnInit("Modified_SF_EntryAttributes.jar");
}
+ public void testJarSigning_removedEntry() throws Exception {
+ assertThrowsOnInit("removed.jar");
+ }
+
private void assertThrowsOnInit(String name) throws Exception {
Support_Resources.copyFile(resources, null, name);
try {
diff --git a/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java
new file mode 100644
index 0000000..9e049c0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2010 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 libcore.java.util.zip;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import junit.framework.TestCase;
+
+import tests.support.resource.Support_Resources;
+
+public abstract class AbstractZipFileTest extends TestCase {
+ /**
+ * Exercise Inflater's ability to refill the zlib's input buffer. As of this
+ * writing, this buffer's max size is 64KiB compressed bytes. We'll write a
+ * full megabyte of uncompressed data, which should be sufficient to exhaust
+ * the buffer. http://b/issue?id=2734751
+ */
+ public void testInflatingFilesRequiringZipRefill() throws IOException {
+ int originalSize = 1024 * 1024;
+ byte[] readBuffer = new byte[8192];
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(f), 1, originalSize, false /* setEntrySize */);
+ ZipFile zipFile = new ZipFile(f);
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ assertTrue("This test needs >64 KiB of compressed data to exercise Inflater",
+ zipEntry.getCompressedSize() > (64 * 1024));
+ InputStream is = zipFile.getInputStream(zipEntry);
+ while (is.read(readBuffer, 0, readBuffer.length) != -1) {}
+ is.close();
+ }
+ zipFile.close();
+ }
+
+ private static void replaceBytes(byte[] buffer, byte[] original, byte[] replacement) {
+ // Gotcha here: original and replacement must be the same length
+ assertEquals(original.length, replacement.length);
+ boolean found;
+ for(int i=0; i < buffer.length - original.length; i++) {
+ found = false;
+ if (buffer[i] == original[0]) {
+ found = true;
+ for (int j=0; j < original.length; j++) {
+ if (buffer[i+j] != original[j]) {
+ found = false;
+ break;
+ }
+ }
+ }
+ if (found) {
+ for (int j=0; j < original.length; j++) {
+ buffer[i+j] = replacement[j];
+ }
+ }
+ }
+ }
+
+ private static void writeBytes(File f, byte[] bytes) throws IOException {
+ FileOutputStream out = new FileOutputStream(f);
+ out.write(bytes);
+ out.close();
+ }
+
+ /**
+ * Make sure we don't fail silently for duplicate entries.
+ * b/8219321
+ */
+ public void testDuplicateEntries() throws Exception {
+ String name1 = "test_file_name1";
+ String name2 = "test_file_name2";
+
+ // Create the good zip file.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream out = createZipOutputStream(baos);
+ out.putNextEntry(new ZipEntry(name2));
+ out.closeEntry();
+ out.putNextEntry(new ZipEntry(name1));
+ out.closeEntry();
+ out.close();
+
+ // Rewrite one of the filenames.
+ byte[] buffer = baos.toByteArray();
+ replaceBytes(buffer, name2.getBytes(), name1.getBytes());
+
+ // Write the result to a file.
+ File badZip = createTemporaryZipFile();
+ writeBytes(badZip, buffer);
+
+ // Check that we refuse to load the modified file.
+ try {
+ ZipFile bad = new ZipFile(badZip);
+ fail();
+ } catch (ZipException expected) {
+ }
+ }
+
+ /**
+ * Make sure the size used for stored zip entires is the uncompressed size.
+ * b/10227498
+ */
+ public void testStoredEntrySize() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream out = createZipOutputStream(baos);
+
+ // Set up a single stored entry.
+ String name = "test_file";
+ int expectedLength = 5;
+ ZipEntry outEntry = new ZipEntry(name);
+ byte[] buffer = new byte[expectedLength];
+ outEntry.setMethod(ZipEntry.STORED);
+ CRC32 crc = new CRC32();
+ crc.update(buffer);
+ outEntry.setCrc(crc.getValue());
+ outEntry.setSize(buffer.length);
+
+ out.putNextEntry(outEntry);
+ out.write(buffer);
+ out.closeEntry();
+ out.close();
+
+ // Write the result to a file.
+ byte[] outBuffer = baos.toByteArray();
+ File zipFile = createTemporaryZipFile();
+ writeBytes(zipFile, outBuffer);
+
+ ZipFile zip = new ZipFile(zipFile);
+ // Set up the zip entry to have different compressed/uncompressed sizes.
+ ZipEntry ze = zip.getEntry(name);
+ ze.setCompressedSize(expectedLength - 1);
+ // Read the contents of the stream and verify uncompressed size was used.
+ InputStream stream = zip.getInputStream(ze);
+ int count = 0;
+ int read;
+ while ((read = stream.read(buffer)) != -1) {
+ count += read;
+ }
+
+ assertEquals(expectedLength, count);
+ zip.close();
+ }
+
+ public void testInflatingStreamsRequiringZipRefill() throws IOException {
+ int originalSize = 1024 * 1024;
+ byte[] readBuffer = new byte[8192];
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(f), 1, originalSize, false /* setEntrySize */);
+
+ ZipInputStream in = new ZipInputStream(new FileInputStream(f));
+ while (in.getNextEntry() != null) {
+ while (in.read(readBuffer, 0, readBuffer.length) != -1) {}
+ }
+ in.close();
+ }
+
+ public void testZipFileWithLotsOfEntries() throws IOException {
+ int expectedEntryCount = 64*1024 - 1;
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(f), expectedEntryCount, 0, false /* setEntrySize */);
+ ZipFile zipFile = new ZipFile(f);
+ int entryCount = 0;
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ ++entryCount;
+ }
+ assertEquals(expectedEntryCount, entryCount);
+ zipFile.close();
+ }
+
+ // http://code.google.com/p/android/issues/detail?id=36187
+ public void testZipFileLargerThan2GiB() throws IOException {
+ if (false) { // TODO: this test requires too much time and too much disk space!
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(f), 1024, 3*1024*1024, false /* setEntrySize */);
+ ZipFile zipFile = new ZipFile(f);
+ int entryCount = 0;
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ e.nextElement();
+ ++entryCount;
+ }
+ assertEquals(1024, entryCount);
+ zipFile.close();
+ }
+ }
+
+ /**
+ * Compresses the given number of files, each of the given size, into a .zip archive.
+ */
+ protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize,
+ boolean setEntrySize)
+ throws IOException {
+ byte[] writeBuffer = new byte[8192];
+ Random random = new Random();
+ try {
+ for (int entry = 0; entry < entryCount; ++entry) {
+ ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+ if (setEntrySize) {
+ ze.setSize(entrySize);
+ }
+ out.putNextEntry(ze);
+
+ for (long i = 0; i < entrySize; i += writeBuffer.length) {
+ random.nextBytes(writeBuffer);
+ int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+ out.write(writeBuffer, 0, byteCount);
+ }
+
+ out.closeEntry();
+ }
+ } finally {
+ out.close();
+ }
+ }
+
+ static File createTemporaryZipFile() throws IOException {
+ File result = File.createTempFile("ZipFileTest", ".zip");
+ result.deleteOnExit();
+ return result;
+ }
+
+ private ZipOutputStream createZipOutputStream(File f) throws IOException {
+ return createZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
+ }
+
+ protected abstract ZipOutputStream createZipOutputStream(OutputStream wrapped);
+
+ public void testSTORED() throws IOException {
+ ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
+ CRC32 crc = new CRC32();
+
+ // Missing CRC, size, and compressed size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Missing CRC and compressed size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setSize(0);
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Missing CRC and size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setSize(0);
+ ze.setCompressedSize(0);
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Missing size and compressed size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Missing size is copied from compressed size.
+ {
+ ZipEntry ze = new ZipEntry("okay1");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+
+ assertEquals(-1, ze.getSize());
+ assertEquals(-1, ze.getCompressedSize());
+
+ ze.setCompressedSize(0);
+
+ assertEquals(-1, ze.getSize());
+ assertEquals(0, ze.getCompressedSize());
+
+ out.putNextEntry(ze);
+
+ assertEquals(0, ze.getSize());
+ assertEquals(0, ze.getCompressedSize());
+ }
+
+ // Missing compressed size is copied from size.
+ {
+ ZipEntry ze = new ZipEntry("okay2");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+
+ assertEquals(-1, ze.getSize());
+ assertEquals(-1, ze.getCompressedSize());
+
+ ze.setSize(0);
+
+ assertEquals(0, ze.getSize());
+ assertEquals(-1, ze.getCompressedSize());
+
+ out.putNextEntry(ze);
+
+ assertEquals(0, ze.getSize());
+ assertEquals(0, ze.getCompressedSize());
+ }
+
+ // Mismatched size and compressed size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+ ze.setCompressedSize(1);
+ ze.setSize(0);
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Everything present => success.
+ ZipEntry ze = new ZipEntry("okay");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+ ze.setSize(0);
+ ze.setCompressedSize(0);
+ out.putNextEntry(ze);
+
+ out.close();
+ }
+
+ private String makeString(int count, String ch) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < count; ++i) {
+ sb.append(ch);
+ }
+ return sb.toString();
+ }
+
+ public void testComments() throws Exception {
+ String expectedFileComment = "1 \u0666 2";
+ String expectedEntryComment = "a \u0666 b";
+
+ File file = createTemporaryZipFile();
+ ZipOutputStream out = createZipOutputStream(file);
+
+ // Is file comment length checking done on bytes or characters? (Should be bytes.)
+ out.setComment(null);
+ out.setComment(makeString(0xffff, "a"));
+ try {
+ out.setComment(makeString(0xffff + 1, "a"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ out.setComment(makeString(0xffff, "\u0666"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ ZipEntry ze = new ZipEntry("a");
+
+ // Is entry comment length checking done on bytes or characters? (Should be bytes.)
+ ze.setComment(null);
+ ze.setComment(makeString(0xffff, "a"));
+ try {
+ ze.setComment(makeString(0xffff + 1, "a"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ ze.setComment(makeString(0xffff, "\u0666"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ ze.setComment(expectedEntryComment);
+ out.putNextEntry(ze);
+ out.closeEntry();
+
+ out.setComment(expectedFileComment);
+ out.close();
+
+ ZipFile zipFile = new ZipFile(file);
+ assertEquals(expectedFileComment, zipFile.getComment());
+ assertEquals(expectedEntryComment, zipFile.getEntry("a").getComment());
+ zipFile.close();
+ }
+
+ public void test_getComment_unset() throws Exception {
+ File file = createTemporaryZipFile();
+ ZipOutputStream out = createZipOutputStream(file);
+ ZipEntry ze = new ZipEntry("test entry");
+ ze.setComment("per-entry comment");
+ out.putNextEntry(ze);
+ out.close();
+
+ ZipFile zipFile = new ZipFile(file);
+ assertEquals(null, zipFile.getComment());
+ }
+
+ // https://code.google.com/p/android/issues/detail?id=58465
+ public void test_NUL_in_filename() throws Exception {
+ File file = createTemporaryZipFile();
+
+ // We allow creation of a ZipEntry whose name contains a NUL byte,
+ // mainly because it's not likely to happen by accident and it's useful for testing.
+ ZipOutputStream out = createZipOutputStream(file);
+ out.putNextEntry(new ZipEntry("hello"));
+ out.putNextEntry(new ZipEntry("hello\u0000"));
+ out.close();
+
+ // But you can't open a ZIP file containing such an entry, because we reject it
+ // when we find it in the central directory.
+ try {
+ ZipFile zipFile = new ZipFile(file);
+ fail();
+ } catch (ZipException expected) {
+ }
+ }
+
+ public void testCrc() throws IOException {
+ ZipEntry ze = new ZipEntry("test");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setSize(4);
+
+ // setCrc takes a long, not an int, so -1 isn't a valid CRC32 (because it's 64 bits).
+ try {
+ ze.setCrc(-1);
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // You can set the CRC32 to 0xffffffff if you're slightly more careful though...
+ ze.setCrc(0xffffffffL);
+ assertEquals(0xffffffffL, ze.getCrc());
+
+ // And it actually works, even though we use -1L to mean "no CRC set"...
+ ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
+ out.putNextEntry(ze);
+ out.write(-1);
+ out.write(-1);
+ out.write(-1);
+ out.write(-1);
+ out.closeEntry();
+ out.close();
+ }
+
+ /**
+ * RI does not allow reading of an empty zip using a {@link ZipFile}.
+ */
+ public void testConstructorFailsWhenReadingEmptyZipArchive() throws IOException {
+
+ File resources = Support_Resources.createTempFolder();
+ File emptyZip = Support_Resources.copyFile(
+ resources, "java/util/zip", "EmptyArchive.zip");
+
+ try {
+ // The following should fail with an exception but if it doesn't then we need to clean
+ // up the resource so we need a reference to it.
+ ZipFile zipFile = new ZipFile(emptyZip);
+
+ // Clean up the resource.
+ try {
+ zipFile.close();
+ } catch (Exception e) {
+ // Ignore
+ }
+ fail();
+ } catch (ZipException expected) {
+ // expected
+ }
+ }
+
+ // Demonstrates http://b/18644314 : Zip entry names are relative to the point of
+ // extraction and can contain relative paths "../" and "./".
+ //
+ // It is left to callers of the API to perform any validation / santization to
+ // ensure that files are not written outside of the destination directory, where that
+ // is a concern.
+ public void testArchivesWithRelativePaths() throws IOException {
+ String[] entryNames = {
+ "../",
+ "../foo.bar",
+ "foo/../../",
+ "foo/../../bar.baz"
+ };
+
+ File zip = createTemporaryZipFile();
+ ZipOutputStream out = createZipOutputStream(zip);
+
+ try {
+ byte[] entryData = new byte[1024];
+ for (String entryName : entryNames) {
+ ZipEntry ze = new ZipEntry(entryName);
+ out.putNextEntry(ze);
+ out.write(entryData);
+ out.closeEntry();
+ }
+ } finally {
+ out.close();
+ }
+
+ ZipFile zf = new ZipFile(zip, ZipFile.OPEN_READ);
+ Enumeration<? extends ZipEntry> entries = zf.entries();
+ Set<String> entryNamesFromFile = new HashSet<>();
+ while (entries.hasMoreElements()) {
+ ZipEntry ze = entries.nextElement();
+ entryNamesFromFile.add(ze.getName());
+ }
+
+ zf.close();
+
+ for (String entryName : entryNames) {
+ assertTrue(entryNamesFromFile.contains(entryName));
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java b/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
index 30aa7f3..1dfa775 100644
--- a/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
@@ -82,4 +82,26 @@ public class DeflaterTest extends TestCase {
assertTrue(totalDeflated > 0); // the deflated form should be non-empty
assertEquals(0, totalInflated);
}
+
+ public void testDeflaterCounts() throws Exception {
+ deflater.setInput(new byte[] { 1, 2, 3 });
+ assertEquals(11, deflater.deflate(compressed, 0, compressed.length, Deflater.FULL_FLUSH));
+ assertEquals(3, deflater.getBytesRead());
+ assertEquals(3, deflater.getTotalIn());
+ assertEquals(11, deflater.getBytesWritten());
+ assertEquals(11, deflater.getTotalOut());
+
+ deflater.setInput(new byte[] { 1, 2, 3 });
+ assertEquals(9, deflater.deflate(compressed, 0, compressed.length, Deflater.FULL_FLUSH));
+ assertEquals(6, deflater.getBytesRead());
+ assertEquals(6, deflater.getTotalIn());
+ assertEquals(20, deflater.getBytesWritten());
+
+
+ deflater.reset();
+ assertEquals(0, deflater.getBytesRead());
+ assertEquals(0, deflater.getBytesWritten());
+ assertEquals(0, deflater.getTotalIn());
+ assertEquals(0, deflater.getTotalOut());
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
index 494520a..5813753 100644
--- a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
@@ -35,14 +35,46 @@ import libcore.io.Streams;
public final class GZIPInputStreamTest extends TestCase {
private static final byte[] HELLO_WORLD_GZIPPED = new byte[] {
- 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -13, 72, -51, -55, -55, 87, 8, -49,
- 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0
+ 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, // 10 byte header
+ -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0 // data
+ };
+
+ /**
+ * This is the same as the above, except that the 4th header byte is 2 (FHCRC flag)
+ * and the 2 bytes after the header make up the CRC.
+ *
+ * Constructed manually because none of the commonly used tools appear to emit header CRCs.
+ */
+ private static final byte[] HELLO_WORLD_GZIPPED_WITH_HEADER_CRC = new byte[] {
+ 31, -117, 8, 2, 0, 0, 0, 0, 0, 0, // 10 byte header
+ 29, 38, // 2 byte CRC.
+ -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0 // data
+ };
+
+ /*(
+ * This is the same as {@code HELLO_WORLD_GZIPPED} except that the 4th header byte is 4
+ * (FEXTRA flag) and that the 8 bytes after the header make up the extra.
+ *
+ * Constructed manually because none of the commonly used tools appear to emit header CRCs.
+ */
+ private static final byte[] HELLO_WORLD_GZIPPED_WITH_EXTRA = new byte[] {
+ 31, -117, 8, 4, 0, 0, 0, 0, 0, 0, // 10 byte header
+ 6, 0, 4, 2, 4, 2, 4, 2, // 2 byte extra length + 6 byte extra.
+ -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0 // data
};
public void testShortMessage() throws IOException {
assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED), "UTF-8"));
}
+ public void testShortMessageWithCrc() throws IOException {
+ assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED_WITH_HEADER_CRC), "UTF-8"));
+ }
+
+ public void testShortMessageWithHeaderExtra() throws IOException {
+ assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED_WITH_EXTRA), "UTF-8"));
+ }
+
public void testLongMessage() throws IOException {
byte[] data = new byte[1024 * 1024];
new Random().nextBytes(data);
diff --git a/luni/src/test/java/libcore/java/util/zip/InflaterTest.java b/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
index 158b3e9..cce08f3 100644
--- a/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
@@ -122,4 +122,35 @@ public class InflaterTest extends TestCase {
adler32.update(bytes);
return (int) adler32.getValue();
}
+
+ public void testInflaterCounts() throws Exception {
+ Inflater inflater = new Inflater();
+
+ byte[] decompressed = new byte[32];
+ byte[] compressed = deflate(new byte[] { 1, 2, 3}, null);
+ assertEquals(11, compressed.length);
+
+ // Feed in bytes [0, 5) to the first iteration.
+ inflater.setInput(compressed, 0, 5);
+ inflater.inflate(decompressed, 0, decompressed.length);
+ assertEquals(5, inflater.getBytesRead());
+ assertEquals(5, inflater.getTotalIn());
+ assertEquals(2, inflater.getBytesWritten());
+ assertEquals(2, inflater.getTotalOut());
+
+ // Feed in bytes [5, 11) to the second iteration.
+ assertEquals(true, inflater.needsInput());
+ inflater.setInput(compressed, 5, 6);
+ assertEquals(1, inflater.inflate(decompressed, 0, decompressed.length));
+ assertEquals(11, inflater.getBytesRead());
+ assertEquals(11, inflater.getTotalIn());
+ assertEquals(3, inflater.getBytesWritten());
+ assertEquals(3, inflater.getTotalOut());
+
+ inflater.reset();
+ assertEquals(0, inflater.getBytesRead());
+ assertEquals(0, inflater.getTotalIn());
+ assertEquals(0, inflater.getBytesWritten());
+ assertEquals(0, inflater.getTotalOut());
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java b/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
new file mode 100644
index 0000000..1b733f9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 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 libcore.java.util.zip;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public final class Zip64FileTest extends AbstractZipFileTest {
+ @Override
+ protected ZipOutputStream createZipOutputStream(OutputStream wrapped) {
+ return new ZipOutputStream(wrapped, true /* forceZip64 */);
+ }
+
+ public void testZip64Support_largeNumberOfEntries() throws IOException {
+ final File file = createZipFile(65550, 2, false /* setEntrySize */);
+ ZipFile zf = null;
+ try {
+ zf = new ZipFile(file);
+ assertEquals(65550, zf.size());
+
+ Enumeration<? extends ZipEntry> entries = zf.entries();
+ assertTrue(entries.hasMoreElements());
+ ZipEntry ze = entries.nextElement();
+ assertEquals(2, ze.getSize());
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ public void testZip64Support_totalLargerThan4G() throws IOException {
+ final File file = createZipFile(5, 1073741824L, false /* setEntrySize */);
+ ZipFile zf = null;
+ try {
+ zf = new ZipFile(file);
+ assertEquals(5, zf.size());
+ Enumeration<? extends ZipEntry> entries = zf.entries();
+ assertTrue(entries.hasMoreElements());
+ ZipEntry ze = entries.nextElement();
+ assertEquals(1073741824L, ze.getSize());
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ public void testZip64Support_hugeEntry() throws IOException {
+ try {
+ createZipFile(1, 4294967410L, false /* setEntrySize */);
+ fail();
+ } catch (IOException expected) {
+ }
+
+ final File file = createZipFile(1, 4294967410L, true /* setEntrySize */);
+ ZipFile zf = null;
+ try {
+ zf = new ZipFile(file);
+ assertEquals(1, zf.size());
+ Enumeration<? extends ZipEntry> entries = zf.entries();
+ assertTrue(entries.hasMoreElements());
+ ZipEntry ze = entries.nextElement();
+ assertEquals(4294967410L, ze.getSize());
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ private File createZipFile(int numEntries, long entrySize, boolean setEntrySize)
+ throws IOException {
+ File file = createTemporaryZipFile();
+ // Don't force a 64 bit zip file to test that our heuristics work.
+ ZipOutputStream os = new ZipOutputStream(
+ new BufferedOutputStream(new FileOutputStream(file)));
+ writeEntries(os, numEntries, entrySize, setEntrySize);
+ return file;
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/Zip64Test.java b/luni/src/test/java/libcore/java/util/zip/Zip64Test.java
new file mode 100644
index 0000000..e4b5baf
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/Zip64Test.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 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 libcore.java.util.zip;
+
+import junit.framework.TestCase;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.zip.Zip64;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+
+public class Zip64Test extends TestCase {
+
+ // We shouldn't attempt to look inside the extended info if we have valid fields
+ // in the regular file header / central directory entry.
+ public void testParseZip64ExtendedInfo_noFieldsPresent() throws Exception {
+ ZipEntry ze = createZipEntry(null, 100, 200, ZipEntry.STORED, 300);
+ Zip64.parseZip64ExtendedInfo(ze, false /* fromCentralDirectory */);
+ Zip64.parseZip64ExtendedInfo(ze, true /* fromCentralDirectory */);
+ }
+
+ // We *should* attempt to look in the extended info if the local file header / central
+ // directory entry don't have the correct values.
+ public void testParseZip64ExtendedInfo_missingExtendedInfo() throws Exception {
+ ZipEntry ze = createZipEntry(null, Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE,
+ Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE, ZipEntry.STORED, 300);
+ try {
+ Zip64.parseZip64ExtendedInfo(ze, false /* fromCentralDirectory */);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ try {
+ Zip64.parseZip64ExtendedInfo(ze, true /* fromCentralDirectory */);
+ fail();
+ } catch (ZipException expected) {
+ }
+ }
+
+ // Test the case where the compressed / uncompressed sizes are in the extended info
+ // but the header offset isn't.
+ public void testParseZip64ExtendedInfo_partialInfo() throws Exception {
+ byte[] extras = new byte[20];
+ ByteBuffer buf = ByteBuffer.wrap(extras);
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+ buf.putShort((short) 0x0001);
+ buf.putShort((short) 16);
+ buf.putLong(50);
+ buf.putLong(100);
+
+ ZipEntry ze = createZipEntry(extras, Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE,
+ Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE, ZipEntry.STORED, 300);
+
+ Zip64.parseZip64ExtendedInfo(ze, false /*fromCentralDirectory */);
+ assertEquals(50, ze.getSize());
+ assertEquals(100, ze.getCompressedSize());
+
+ ze = createZipEntry(extras, Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE,
+ Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE, ZipEntry.STORED, 300);
+ Zip64.parseZip64ExtendedInfo(ze, true /*fromCentralDirectory */);
+ assertEquals(50, ze.getSize());
+ assertEquals(100, ze.getCompressedSize());
+ }
+
+ public void testInsertZip64ExtendedInfo() throws Exception {
+ ZipEntry ze = createZipEntry(null, Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE + 300,
+ Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE + 500, ZipEntry.STORED, 300);
+ Zip64.insertZip64ExtendedInfoToExtras(ze);
+
+ assertNotNull(ze.getExtra());
+ ByteBuffer buf = ByteBuffer.wrap(ze.getExtra());
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+ assertEquals(0x0001, buf.getShort());
+ assertEquals(24, buf.getShort());
+ assertEquals(Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE + 300, buf.getLong());
+ assertEquals(Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE + 500, buf.getLong());
+ }
+
+ private static ZipEntry createZipEntry(byte[] extras, long size, long compressedSize,
+ int compressionMethod, long headerOffset) {
+ return new ZipEntry("name", "comment", 42 /* crc */, compressedSize, size,
+ compressionMethod, 42 /* time */, 42 /* modDate */, extras, headerOffset,
+ 42 /* data offset */);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
index 550ddfb..c7667b9 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
@@ -25,6 +25,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@@ -132,6 +133,7 @@ public class ZipEntryTest extends junit.framework.TestCase {
File f = createTemporaryZipFile();
ZipOutputStream out = createZipOutputStream(f);
ZipEntry ze = new ZipEntry("x");
+ ze.setSize(0);
ze.setExtra(maxLengthExtra);
out.putNextEntry(ze);
out.closeEntry();
@@ -143,6 +145,25 @@ public class ZipEntryTest extends junit.framework.TestCase {
zipFile.close();
}
+ public void testMaxLengthExtra_zip64() throws Exception {
+ // Not quite the max length (65535), but large enough that there's no space
+ // for the zip64 extended info header.
+ byte[] maxLengthExtra = new byte[65530];
+
+ File f = createTemporaryZipFile();
+ ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)),
+ true /* forceZip64 */);
+ ZipEntry ze = new ZipEntry("x");
+
+ ze.setExtra(maxLengthExtra);
+ try {
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+ }
+
+
public void testTooLongComment() throws Exception {
String tooLongComment = makeString(65536, "z");
ZipEntry ze = new ZipEntry("x");
@@ -176,7 +197,17 @@ public class ZipEntryTest extends junit.framework.TestCase {
File f = createTemporaryZipFile();
ZipOutputStream out = createZipOutputStream(f);
+
+ // Regular (non zip64) format.
ZipEntry ze = new ZipEntry("x");
+ ze.setSize(0);
+ ze.setExtra(extra);
+ ze.setComment(comment);
+ out.putNextEntry(ze);
+ out.closeEntry();
+
+ // An entry without a length is assumed to be zip64.
+ ze = new ZipEntry("y");
ze.setExtra(extra);
ze.setComment(comment);
out.putNextEntry(ze);
@@ -188,6 +219,9 @@ public class ZipEntryTest extends junit.framework.TestCase {
try {
assertEquals(comment, zipFile.getEntry("x").getComment());
assertTrue(Arrays.equals(extra, zipFile.getEntry("x").getExtra()));
+
+ assertEquals(comment, zipFile.getEntry("y").getComment());
+ assertTrue(Arrays.equals(extra, zipFile.getEntry("y").getExtra()));
} finally {
zipFile.close();
}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index a9ff56f..02210ac 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -11,497 +11,18 @@
* 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.
+ * limitations under the License
*/
package libcore.java.util.zip;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-import java.util.zip.CRC32;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
-import junit.framework.TestCase;
-import tests.support.resource.Support_Resources;
+public final class ZipFileTest extends AbstractZipFileTest {
-public final class ZipFileTest extends TestCase {
- /**
- * Exercise Inflater's ability to refill the zlib's input buffer. As of this
- * writing, this buffer's max size is 64KiB compressed bytes. We'll write a
- * full megabyte of uncompressed data, which should be sufficient to exhaust
- * the buffer. http://b/issue?id=2734751
- */
- public void testInflatingFilesRequiringZipRefill() throws IOException {
- int originalSize = 1024 * 1024;
- byte[] readBuffer = new byte[8192];
- ZipFile zipFile = new ZipFile(createZipFile(1, originalSize));
- for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
- ZipEntry zipEntry = e.nextElement();
- assertTrue("This test needs >64 KiB of compressed data to exercise Inflater",
- zipEntry.getCompressedSize() > (64 * 1024));
- InputStream is = zipFile.getInputStream(zipEntry);
- while (is.read(readBuffer, 0, readBuffer.length) != -1) {}
- is.close();
- }
- zipFile.close();
- }
-
- private static void replaceBytes(byte[] buffer, byte[] original, byte[] replacement) {
- // Gotcha here: original and replacement must be the same length
- assertEquals(original.length, replacement.length);
- boolean found;
- for(int i=0; i < buffer.length - original.length; i++) {
- found = false;
- if (buffer[i] == original[0]) {
- found = true;
- for (int j=0; j < original.length; j++) {
- if (buffer[i+j] != original[j]) {
- found = false;
- break;
- }
- }
- }
- if (found) {
- for (int j=0; j < original.length; j++) {
- buffer[i+j] = replacement[j];
- }
- }
- }
- }
-
- private static void writeBytes(File f, byte[] bytes) throws IOException {
- FileOutputStream out = new FileOutputStream(f);
- out.write(bytes);
- out.close();
- }
-
- /**
- * Make sure we don't fail silently for duplicate entries.
- * b/8219321
- */
- public void testDuplicateEntries() throws Exception {
- String name1 = "test_file_name1";
- String name2 = "test_file_name2";
-
- // Create the good zip file.
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ZipOutputStream out = new ZipOutputStream(baos);
- out.putNextEntry(new ZipEntry(name2));
- out.closeEntry();
- out.putNextEntry(new ZipEntry(name1));
- out.closeEntry();
- out.close();
-
- // Rewrite one of the filenames.
- byte[] buffer = baos.toByteArray();
- replaceBytes(buffer, name2.getBytes(), name1.getBytes());
-
- // Write the result to a file.
- File badZip = createTemporaryZipFile();
- writeBytes(badZip, buffer);
-
- // Check that we refuse to load the modified file.
- try {
- ZipFile bad = new ZipFile(badZip);
- fail();
- } catch (ZipException expected) {
- }
- }
-
- /**
- * Make sure the size used for stored zip entires is the uncompressed size.
- * b/10227498
- */
- public void testStoredEntrySize() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ZipOutputStream out = new ZipOutputStream(baos);
-
- // Set up a single stored entry.
- String name = "test_file";
- int expectedLength = 5;
- ZipEntry outEntry = new ZipEntry(name);
- byte[] buffer = new byte[expectedLength];
- outEntry.setMethod(ZipEntry.STORED);
- CRC32 crc = new CRC32();
- crc.update(buffer);
- outEntry.setCrc(crc.getValue());
- outEntry.setSize(buffer.length);
-
- out.putNextEntry(outEntry);
- out.write(buffer);
- out.closeEntry();
- out.close();
-
- // Write the result to a file.
- byte[] outBuffer = baos.toByteArray();
- File zipFile = createTemporaryZipFile();
- writeBytes(zipFile, outBuffer);
-
- ZipFile zip = new ZipFile(zipFile);
- // Set up the zip entry to have different compressed/uncompressed sizes.
- ZipEntry ze = zip.getEntry(name);
- ze.setCompressedSize(expectedLength - 1);
- // Read the contents of the stream and verify uncompressed size was used.
- InputStream stream = zip.getInputStream(ze);
- int count = 0;
- int read;
- while ((read = stream.read(buffer)) != -1) {
- count += read;
- }
-
- assertEquals(expectedLength, count);
- zip.close();
- }
-
- public void testInflatingStreamsRequiringZipRefill() throws IOException {
- int originalSize = 1024 * 1024;
- byte[] readBuffer = new byte[8192];
- ZipInputStream in = new ZipInputStream(new FileInputStream(createZipFile(1, originalSize)));
- while (in.getNextEntry() != null) {
- while (in.read(readBuffer, 0, readBuffer.length) != -1) {}
- }
- in.close();
- }
-
- public void testZipFileWithLotsOfEntries() throws IOException {
- int expectedEntryCount = 64*1024 - 1;
- File f = createZipFile(expectedEntryCount, 0);
- ZipFile zipFile = new ZipFile(f);
- int entryCount = 0;
- for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
- ZipEntry zipEntry = e.nextElement();
- ++entryCount;
- }
- assertEquals(expectedEntryCount, entryCount);
- zipFile.close();
- }
-
- // http://code.google.com/p/android/issues/detail?id=36187
- public void testZipFileLargerThan2GiB() throws IOException {
- if (false) { // TODO: this test requires too much time and too much disk space!
- File f = createZipFile(1024, 3*1024*1024);
- ZipFile zipFile = new ZipFile(f);
- int entryCount = 0;
- for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
- ZipEntry zipEntry = e.nextElement();
- ++entryCount;
- }
- assertEquals(1024, entryCount);
- zipFile.close();
- }
- }
-
- public void testZip64Support() throws IOException {
- try {
- createZipFile(64*1024, 0);
- fail(); // Make this test more like testHugeZipFile when we have Zip64 support.
- } catch (ZipException expected) {
- }
- }
-
- /**
- * Compresses the given number of files, each of the given size, into a .zip archive.
- */
- private File createZipFile(int entryCount, int entrySize) throws IOException {
- File result = createTemporaryZipFile();
-
- byte[] writeBuffer = new byte[8192];
- Random random = new Random();
-
- ZipOutputStream out = createZipOutputStream(result);
- try {
- for (int entry = 0; entry < entryCount; ++entry) {
- ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
- out.putNextEntry(ze);
-
- for (int i = 0; i < entrySize; i += writeBuffer.length) {
- random.nextBytes(writeBuffer);
- int byteCount = Math.min(writeBuffer.length, entrySize - i);
- out.write(writeBuffer, 0, byteCount);
- }
-
- out.closeEntry();
- }
- } finally {
- out.close();
- }
- return result;
- }
-
- private File createTemporaryZipFile() throws IOException {
- File result = File.createTempFile("ZipFileTest", "zip");
- result.deleteOnExit();
- return result;
- }
-
- private ZipOutputStream createZipOutputStream(File f) throws IOException {
- return new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
- }
-
- public void testSTORED() throws IOException {
- ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
- CRC32 crc = new CRC32();
-
- // Missing CRC, size, and compressed size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Missing CRC and compressed size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- ze.setSize(0);
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Missing CRC and size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- ze.setSize(0);
- ze.setCompressedSize(0);
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Missing size and compressed size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Missing size is copied from compressed size.
- {
- ZipEntry ze = new ZipEntry("okay1");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
-
- assertEquals(-1, ze.getSize());
- assertEquals(-1, ze.getCompressedSize());
-
- ze.setCompressedSize(0);
-
- assertEquals(-1, ze.getSize());
- assertEquals(0, ze.getCompressedSize());
-
- out.putNextEntry(ze);
-
- assertEquals(0, ze.getSize());
- assertEquals(0, ze.getCompressedSize());
- }
-
- // Missing compressed size is copied from size.
- {
- ZipEntry ze = new ZipEntry("okay2");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
-
- assertEquals(-1, ze.getSize());
- assertEquals(-1, ze.getCompressedSize());
-
- ze.setSize(0);
-
- assertEquals(0, ze.getSize());
- assertEquals(-1, ze.getCompressedSize());
-
- out.putNextEntry(ze);
-
- assertEquals(0, ze.getSize());
- assertEquals(0, ze.getCompressedSize());
- }
-
- // Mismatched size and compressed size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
- ze.setCompressedSize(1);
- ze.setSize(0);
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Everything present => success.
- ZipEntry ze = new ZipEntry("okay");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
- ze.setSize(0);
- ze.setCompressedSize(0);
- out.putNextEntry(ze);
-
- out.close();
- }
-
- private String makeString(int count, String ch) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < count; ++i) {
- sb.append(ch);
- }
- return sb.toString();
- }
-
- public void testComments() throws Exception {
- String expectedFileComment = "1 \u0666 2";
- String expectedEntryComment = "a \u0666 b";
-
- File file = createTemporaryZipFile();
- ZipOutputStream out = createZipOutputStream(file);
-
- // Is file comment length checking done on bytes or characters? (Should be bytes.)
- out.setComment(null);
- out.setComment(makeString(0xffff, "a"));
- try {
- out.setComment(makeString(0xffff + 1, "a"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- out.setComment(makeString(0xffff, "\u0666"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- ZipEntry ze = new ZipEntry("a");
-
- // Is entry comment length checking done on bytes or characters? (Should be bytes.)
- ze.setComment(null);
- ze.setComment(makeString(0xffff, "a"));
- try {
- ze.setComment(makeString(0xffff + 1, "a"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- ze.setComment(makeString(0xffff, "\u0666"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- ze.setComment(expectedEntryComment);
- out.putNextEntry(ze);
- out.closeEntry();
-
- out.setComment(expectedFileComment);
- out.close();
-
- ZipFile zipFile = new ZipFile(file);
- assertEquals(expectedFileComment, zipFile.getComment());
- assertEquals(expectedEntryComment, zipFile.getEntry("a").getComment());
- zipFile.close();
- }
-
- public void test_getComment_unset() throws Exception {
- File file = createTemporaryZipFile();
- ZipOutputStream out = createZipOutputStream(file);
- ZipEntry ze = new ZipEntry("test entry");
- ze.setComment("per-entry comment");
- out.putNextEntry(ze);
- out.close();
-
- ZipFile zipFile = new ZipFile(file);
- assertEquals(null, zipFile.getComment());
- }
-
- // https://code.google.com/p/android/issues/detail?id=58465
- public void test_NUL_in_filename() throws Exception {
- File file = createTemporaryZipFile();
-
- // We allow creation of a ZipEntry whose name contains a NUL byte,
- // mainly because it's not likely to happen by accident and it's useful for testing.
- ZipOutputStream out = createZipOutputStream(file);
- out.putNextEntry(new ZipEntry("hello"));
- out.putNextEntry(new ZipEntry("hello\u0000"));
- out.close();
-
- // But you can't open a ZIP file containing such an entry, because we reject it
- // when we find it in the central directory.
- try {
- ZipFile zipFile = new ZipFile(file);
- fail();
- } catch (ZipException expected) {
- }
- }
-
- public void testCrc() throws IOException {
- ZipEntry ze = new ZipEntry("test");
- ze.setMethod(ZipEntry.STORED);
- ze.setSize(4);
-
- // setCrc takes a long, not an int, so -1 isn't a valid CRC32 (because it's 64 bits).
- try {
- ze.setCrc(-1);
- } catch (IllegalArgumentException expected) {
- }
-
- // You can set the CRC32 to 0xffffffff if you're slightly more careful though...
- ze.setCrc(0xffffffffL);
- assertEquals(0xffffffffL, ze.getCrc());
-
- // And it actually works, even though we use -1L to mean "no CRC set"...
- ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
- out.putNextEntry(ze);
- out.write(-1);
- out.write(-1);
- out.write(-1);
- out.write(-1);
- out.closeEntry();
- out.close();
- }
-
- /**
- * RI does not allow reading of an empty zip using a {@link ZipFile}.
- */
- public void testConstructorFailsWhenReadingEmptyZipArchive() throws IOException {
-
- File resources = Support_Resources.createTempFolder();
- File emptyZip = Support_Resources.copyFile(
- resources, "java/util/zip", "EmptyArchive.zip");
-
- try {
- // The following should fail with an exception but if it doesn't then we need to clean
- // up the resource so we need a reference to it.
- ZipFile zipFile = new ZipFile(emptyZip);
-
- // Clean up the resource.
- try {
- zipFile.close();
- } catch (Exception e) {
- // Ignore
- }
- fail();
- } catch (ZipException expected) {
- // expected
- }
+ @Override
+ protected ZipOutputStream createZipOutputStream(OutputStream wrapped) {
+ return new ZipOutputStream(wrapped);
}
}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
index e69f010..15600de 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
@@ -21,7 +21,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;
import java.util.zip.ZipEntry;
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index c89886c..dd7d6e7 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -21,11 +21,13 @@ import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
@@ -45,6 +47,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import javax.crypto.AEADBadTagException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
@@ -231,6 +234,10 @@ public final class CipherTest extends TestCase {
return algorithm.startsWith("PBE");
}
+ private static boolean isAEAD(String algorithm) {
+ return "GCM".equals(algorithm) || algorithm.contains("/GCM/");
+ }
+
private static boolean isStreamMode(String algorithm) {
return algorithm.contains("/CTR/") || algorithm.contains("/OFB")
|| algorithm.contains("/CFB");
@@ -295,10 +302,10 @@ public final class CipherTest extends TestCase {
setExpectedBlockSize("AES/ECB/PKCS5PADDING", 16);
setExpectedBlockSize("AES/ECB/PKCS7PADDING", 16);
setExpectedBlockSize("AES/ECB/NOPADDING", 16);
+ setExpectedBlockSize("AES/GCM/NOPADDING", 16);
setExpectedBlockSize("AES/OFB/PKCS5PADDING", 16);
setExpectedBlockSize("AES/OFB/PKCS7PADDING", 16);
setExpectedBlockSize("AES/OFB/NOPADDING", 16);
- setExpectedBlockSize("GCM", 16);
setExpectedBlockSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
setExpectedBlockSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
setExpectedBlockSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
@@ -453,9 +460,9 @@ public final class CipherTest extends TestCase {
setExpectedOutputSize("AES/CTS/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
- setExpectedOutputSize("GCM", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
@@ -486,9 +493,9 @@ public final class CipherTest extends TestCase {
setExpectedOutputSize("AES/CTS/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
- setExpectedOutputSize("GCM", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
@@ -498,15 +505,8 @@ public final class CipherTest extends TestCase {
setExpectedOutputSize("PBEWITHSHAAND128BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHSHAAND192BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
- // AndroidOpenSSL returns the block size for the block ciphers
- setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
- setExpectedOutputSize("AES/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
- setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
- setExpectedOutputSize("AES/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
- setExpectedOutputSize("DESEDE/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 8);
- setExpectedOutputSize("DESEDE/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 8);
- setExpectedOutputSize("DESEDE/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 8);
- setExpectedOutputSize("DESEDE/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 8);
+ setExpectedOutputSize("DESEDE/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 0);
+ setExpectedOutputSize("DESEDE/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 0);
if (StandardNames.IS_RI) {
setExpectedOutputSize("AESWRAP", Cipher.WRAP_MODE, 8);
@@ -750,8 +750,8 @@ public final class CipherTest extends TestCase {
new SecureRandom().nextBytes(salt);
return new PBEParameterSpec(salt, 1024);
}
- if (algorithm.equals("GCM")) {
- final byte[] iv = new byte[8];
+ if (algorithm.equals("AES/GCM/NOPADDING")) {
+ final byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
}
@@ -791,7 +791,7 @@ public final class CipherTest extends TestCase {
}
byte[] iv = encryptCipher.getIV();
if (iv != null) {
- if ("GCM".equals(algorithm)) {
+ if ("AES/GCM/NOPADDING".equals(algorithm)) {
return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
}
return new IvParameterSpec(iv);
@@ -847,6 +847,24 @@ public final class CipherTest extends TestCase {
}
}
+ public void testCipher_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", "None");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Cipher c = Cipher.getInstance("FOO", mockProvider);
+ c.init(Cipher.ENCRYPT_MODE, new MockKey());
+ assertEquals(mockProvider, c.getProvider());
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
public void testCipher_getInstance_SuppliedProviderNotRegistered_MultipartTransform_Success()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
@@ -970,14 +988,130 @@ public final class CipherTest extends TestCase {
Security.addProvider(mockProviderInvalid);
try {
- Cipher.getInstance("FOO");
- fail("Should not find any matching providers");
- } catch (NoSuchAlgorithmException expected) {
+ Cipher c = Cipher.getInstance("FOO");
+ c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[16], "FOO"));
+ fail("Should not find any matching providers; found: " + c);
+ } catch (ClassCastException expected) {
} finally {
Security.removeProvider(mockProviderInvalid.getName());
}
}
+ public void testCipher_init_CallsInitWithParams_AlgorithmParameterSpec() throws Exception {
+ Provider mockProviderRejects = new MockProvider("MockProviderRejects") {
+ public void setup() {
+ put("Cipher.FOO",
+ MockCipherSpi.MustInitWithAlgorithmParameterSpec_RejectsAll.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+ Provider mockProviderAccepts = new MockProvider("MockProviderAccepts") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderRejects);
+ Security.addProvider(mockProviderAccepts);
+ try {
+ Cipher c = Cipher.getInstance("FOO");
+ c.init(Cipher.ENCRYPT_MODE, new MockKey(), new IvParameterSpec(new byte[12]));
+ assertEquals(mockProviderAccepts, c.getProvider());
+ } finally {
+ Security.removeProvider(mockProviderRejects.getName());
+ Security.removeProvider(mockProviderAccepts.getName());
+ }
+ }
+
+ public void testCipher_init_CallsInitWithParams_AlgorithmParameters() throws Exception {
+ Provider mockProviderRejects = new MockProvider("MockProviderRejects") {
+ public void setup() {
+ put("Cipher.FOO",
+ MockCipherSpi.MustInitWithAlgorithmParameters_RejectsAll.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+ Provider mockProviderAccepts = new MockProvider("MockProviderAccepts") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderRejects);
+ Security.addProvider(mockProviderAccepts);
+ try {
+ Cipher c = Cipher.getInstance("FOO");
+ c.init(Cipher.ENCRYPT_MODE, new MockKey(), AlgorithmParameters.getInstance("AES"));
+ assertEquals(mockProviderAccepts, c.getProvider());
+ } finally {
+ Security.removeProvider(mockProviderRejects.getName());
+ Security.removeProvider(mockProviderAccepts.getName());
+ }
+ }
+
+ public void testCipher_init_CallsInitIgnoresRuntimeException() throws Exception {
+ Provider mockProviderRejects = new MockProvider("MockProviderRejects") {
+ public void setup() {
+ put("Cipher.FOO",
+ MockCipherSpi.MustInitWithAlgorithmParameters_ThrowsNull.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+ Provider mockProviderAccepts = new MockProvider("MockProviderAccepts") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderRejects);
+ Security.addProvider(mockProviderAccepts);
+ try {
+ Cipher c = Cipher.getInstance("FOO");
+ c.init(Cipher.ENCRYPT_MODE, new MockKey(), AlgorithmParameters.getInstance("AES"));
+ assertEquals(mockProviderAccepts, c.getProvider());
+ } finally {
+ Security.removeProvider(mockProviderRejects.getName());
+ Security.removeProvider(mockProviderAccepts.getName());
+ }
+ }
+
+ public void testCipher_init_CallsInitWithMode() throws Exception {
+ Provider mockProviderOnlyEncrypt = new MockProvider("MockProviderOnlyEncrypt") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.MustInitForEncryptModeOrRejects.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+ Provider mockProviderAcceptsAll = new MockProvider("MockProviderAcceptsAll") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProviderOnlyEncrypt);
+ Security.addProvider(mockProviderAcceptsAll);
+ try {
+ {
+ Cipher c = Cipher.getInstance("FOO");
+ c.init(Cipher.DECRYPT_MODE, new MockKey(), AlgorithmParameters.getInstance("AES"));
+ assertEquals(mockProviderAcceptsAll, c.getProvider());
+ }
+
+ {
+ Cipher c = Cipher.getInstance("FOO");
+ c.init(Cipher.ENCRYPT_MODE, new MockKey(), AlgorithmParameters.getInstance("AES"));
+ assertEquals(mockProviderOnlyEncrypt, c.getProvider());
+ }
+ } finally {
+ Security.removeProvider(mockProviderOnlyEncrypt.getName());
+ Security.removeProvider(mockProviderAcceptsAll.getName());
+ }
+ }
+
public void test_getInstance() throws Exception {
final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
PrintStream out = new PrintStream(errBuffer);
@@ -1125,9 +1259,9 @@ public final class CipherTest extends TestCase {
c.init(encryptMode, encryptKey, encryptSpec);
assertEquals(cipherID + " getBlockSize() encryptMode",
- getExpectedBlockSize(algorithm, encryptMode, providerName), c.getBlockSize());
- assertEquals(cipherID + " getOutputSize(0) encryptMode",
- getExpectedOutputSize(algorithm, encryptMode, providerName), c.getOutputSize(0));
+ getExpectedBlockSize(algorithm, encryptMode, providerName), c.getBlockSize());
+ assertTrue(cipherID + " getOutputSize(0) encryptMode",
+ getExpectedOutputSize(algorithm, encryptMode, providerName) <= c.getOutputSize(0));
if ((algorithm.endsWith("/PKCS5PADDING") || algorithm.endsWith("/PKCS7PADDING"))
&& isStreamMode(algorithm)) {
assertEquals(getExpectedOutputSize(algorithm, encryptMode, providerName),
@@ -1136,6 +1270,9 @@ public final class CipherTest extends TestCase {
final AlgorithmParameterSpec decryptSpec = getDecryptAlgorithmParameterSpec(encryptSpec, c);
int decryptMode = getDecryptMode(algorithm);
+
+ test_Cipher_init_Decrypt_NullParameters(c, decryptMode, encryptKey, decryptSpec != null);
+
c.init(decryptMode, encryptKey, decryptSpec);
assertEquals(cipherID + " getBlockSize() decryptMode",
getExpectedBlockSize(algorithm, decryptMode, providerName), c.getBlockSize());
@@ -1188,7 +1325,7 @@ public final class CipherTest extends TestCase {
// Test wrapping a key. Every cipher should be able to wrap. Except those that can't.
/* Bouncycastle is broken for wrapping because getIV() fails. */
if (isSupportedForWrapping(algorithm)
- && !algorithm.equals("GCM") && !providerName.equals("BC")) {
+ && !algorithm.equals("AES/GCM/NOPADDING") && !providerName.equals("BC")) {
// Generate a small SecretKey for AES.
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
@@ -1212,16 +1349,28 @@ public final class CipherTest extends TestCase {
if (!isOnlyWrappingAlgorithm(algorithm)) {
c.init(Cipher.ENCRYPT_MODE, encryptKey, encryptSpec);
+ if (isAEAD(algorithm)) {
+ c.updateAAD(new byte[24]);
+ }
byte[] cipherText = c.doFinal(getActualPlainText(algorithm));
+ if (isAEAD(algorithm)) {
+ c.updateAAD(new byte[24]);
+ }
byte[] cipherText2 = c.doFinal(getActualPlainText(algorithm));
assertEquals(cipherID,
Arrays.toString(cipherText),
Arrays.toString(cipherText2));
c.init(Cipher.DECRYPT_MODE, getDecryptKey(algorithm), decryptSpec);
+ if (isAEAD(algorithm)) {
+ c.updateAAD(new byte[24]);
+ }
byte[] decryptedPlainText = c.doFinal(cipherText);
assertEquals(cipherID,
Arrays.toString(getExpectedPlainText(algorithm, providerName)),
Arrays.toString(decryptedPlainText));
+ if (isAEAD(algorithm)) {
+ c.updateAAD(new byte[24]);
+ }
byte[] decryptedPlainText2 = c.doFinal(cipherText);
assertEquals(cipherID,
Arrays.toString(decryptedPlainText),
@@ -1268,6 +1417,53 @@ public final class CipherTest extends TestCase {
}
}
+ private void test_Cipher_init_Decrypt_NullParameters(Cipher c, int decryptMode, Key encryptKey,
+ boolean needsParameters) throws Exception {
+ try {
+ c.init(decryptMode, encryptKey, (AlgorithmParameterSpec) null);
+ if (needsParameters) {
+ fail("Should throw InvalidAlgorithmParameterException with null parameters");
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ if (!needsParameters) {
+ throw e;
+ }
+ }
+
+ try {
+ c.init(decryptMode, encryptKey, (AlgorithmParameterSpec) null, (SecureRandom) null);
+ if (needsParameters) {
+ fail("Should throw InvalidAlgorithmParameterException with null parameters");
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ if (!needsParameters) {
+ throw e;
+ }
+ }
+
+ try {
+ c.init(decryptMode, encryptKey, (AlgorithmParameters) null);
+ if (needsParameters) {
+ fail("Should throw InvalidAlgorithmParameterException with null parameters");
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ if (!needsParameters) {
+ throw e;
+ }
+ }
+
+ try {
+ c.init(decryptMode, encryptKey, (AlgorithmParameters) null, (SecureRandom) null);
+ if (needsParameters) {
+ fail("Should throw InvalidAlgorithmParameterException with null parameters");
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ if (!needsParameters) {
+ throw e;
+ }
+ }
+ }
+
public void testInputPKCS1Padding() throws Exception {
for (String provider : RSA_PROVIDERS) {
testInputPKCS1Padding(provider);
@@ -1293,11 +1489,22 @@ public final class CipherTest extends TestCase {
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/NoPadding", provider);
encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey);
byte[] cipherText = encryptCipher.doFinal(prePaddedPlainText);
+ encryptCipher.update(prePaddedPlainText);
+ encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey);
+ byte[] cipherText2 = encryptCipher.doFinal(prePaddedPlainText);
+ assertEquals(Arrays.toString(cipherText),
+ Arrays.toString(cipherText2));
+
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey);
byte[] plainText = decryptCipher.doFinal(cipherText);
assertEquals(Arrays.toString(ORIGINAL_PLAIN_TEXT),
Arrays.toString(plainText));
+ decryptCipher.update(prePaddedPlainText);
+ decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey);
+ byte[] plainText2 = decryptCipher.doFinal(cipherText);
+ assertEquals(Arrays.toString(plainText),
+ Arrays.toString(plainText2));
}
public void testOutputPKCS1Padding() throws Exception {
@@ -2423,6 +2630,80 @@ public final class CipherTest extends TestCase {
};
/*
+ * Taken from BoringSSL test vectors.
+ */
+ private static final byte[] AES_128_GCM_TestVector_1_Key = new byte[] {
+ (byte) 0xca, (byte) 0xbd, (byte) 0xcf, (byte) 0x54, (byte) 0x1a, (byte) 0xeb,
+ (byte) 0xf9, (byte) 0x17, (byte) 0xba, (byte) 0xc0, (byte) 0x19, (byte) 0xf1,
+ (byte) 0x39, (byte) 0x25, (byte) 0xd2, (byte) 0x67,
+ };
+
+ /*
+ * Taken from BoringSSL test vectors.
+ */
+ private static final byte[] AES_128_GCM_TestVector_1_IV = new byte[] {
+ (byte) 0x2c, (byte) 0x34, (byte) 0xc0, (byte) 0x0c, (byte) 0x42, (byte) 0xda,
+ (byte) 0xe3, (byte) 0x82, (byte) 0x27, (byte) 0x9d, (byte) 0x79, (byte) 0x74,
+ };
+
+ /*
+ * Taken from BoringSSL test vectors.
+ */
+ private static final byte[] AES_128_GCM_TestVector_1_AAD = new byte[] {
+ (byte) 0xdd, (byte) 0x10, (byte) 0xe3, (byte) 0x71, (byte) 0xb2, (byte) 0x2e,
+ (byte) 0x15, (byte) 0x67, (byte) 0x1c, (byte) 0x31, (byte) 0xaf, (byte) 0xee,
+ (byte) 0x55, (byte) 0x2b, (byte) 0xf1, (byte) 0xde, (byte) 0xa0, (byte) 0x7c,
+ (byte) 0xbb, (byte) 0xf6, (byte) 0x85, (byte) 0xe2, (byte) 0xca, (byte) 0xa0,
+ (byte) 0xe0, (byte) 0x36, (byte) 0x37, (byte) 0x16, (byte) 0xa2, (byte) 0x76,
+ (byte) 0xe1, (byte) 0x20, (byte) 0xc6, (byte) 0xc0, (byte) 0xeb, (byte) 0x4a,
+ (byte) 0xcb, (byte) 0x1a, (byte) 0x4d, (byte) 0x1b, (byte) 0xa7, (byte) 0x3f,
+ (byte) 0xde, (byte) 0x66, (byte) 0x15, (byte) 0xf7, (byte) 0x08, (byte) 0xaa,
+ (byte) 0xa4, (byte) 0x6b, (byte) 0xc7, (byte) 0x6c, (byte) 0x7f, (byte) 0xf3,
+ (byte) 0x45, (byte) 0xa4, (byte) 0xf7, (byte) 0x6b, (byte) 0xda, (byte) 0x11,
+ (byte) 0x7f, (byte) 0xe5, (byte) 0x6f, (byte) 0x0d, (byte) 0xc9, (byte) 0xb9,
+ (byte) 0x39, (byte) 0x04, (byte) 0x0d, (byte) 0xdd,
+ };
+
+ /*
+ * Taken from BoringSSL test vectors.
+ */
+ private static final byte[] AES_128_GCM_TestVector_1_Plaintext = new byte[] {
+ (byte) 0x88, (byte) 0xcc, (byte) 0x1e, (byte) 0x07, (byte) 0xdf, (byte) 0xde,
+ (byte) 0x8e, (byte) 0x08, (byte) 0x08, (byte) 0x2e, (byte) 0x67, (byte) 0x66,
+ (byte) 0xe0, (byte) 0xa8, (byte) 0x81, (byte) 0x03, (byte) 0x38, (byte) 0x47,
+ (byte) 0x42, (byte) 0xaf, (byte) 0x37, (byte) 0x8d, (byte) 0x7b, (byte) 0x6b,
+ (byte) 0x8a, (byte) 0x87, (byte) 0xfc, (byte) 0xe0, (byte) 0x36, (byte) 0xaf,
+ (byte) 0x74, (byte) 0x41, (byte) 0xc1, (byte) 0x39, (byte) 0x61, (byte) 0xc2,
+ (byte) 0x5a, (byte) 0xfe, (byte) 0xa7, (byte) 0xf6, (byte) 0xe5, (byte) 0x61,
+ (byte) 0x93, (byte) 0xf5, (byte) 0x4b, (byte) 0xee, (byte) 0x00, (byte) 0x11,
+ (byte) 0xcb, (byte) 0x78, (byte) 0x64, (byte) 0x2c, (byte) 0x3a, (byte) 0xb9,
+ (byte) 0xe6, (byte) 0xd5, (byte) 0xb2, (byte) 0xe3, (byte) 0x58, (byte) 0x33,
+ (byte) 0xec, (byte) 0x16, (byte) 0xcd, (byte) 0x35, (byte) 0x55, (byte) 0x15,
+ (byte) 0xaf, (byte) 0x1a, (byte) 0x19, (byte) 0x0f,
+ };
+
+ /*
+ * Taken from BoringSSL test vectors.
+ */
+ private static final byte[] AES_128_GCM_TestVector_1_Encrypted = new byte[] {
+ (byte) 0x04, (byte) 0x94, (byte) 0x53, (byte) 0xba, (byte) 0xf1, (byte) 0x57,
+ (byte) 0x87, (byte) 0x87, (byte) 0xd6, (byte) 0x8e, (byte) 0xd5, (byte) 0x47,
+ (byte) 0x87, (byte) 0x26, (byte) 0xc0, (byte) 0xb8, (byte) 0xa6, (byte) 0x36,
+ (byte) 0x33, (byte) 0x7a, (byte) 0x0b, (byte) 0x8a, (byte) 0x82, (byte) 0xb8,
+ (byte) 0x68, (byte) 0x36, (byte) 0xf9, (byte) 0x1c, (byte) 0xde, (byte) 0x25,
+ (byte) 0xe6, (byte) 0xe4, (byte) 0x4c, (byte) 0x34, (byte) 0x59, (byte) 0x40,
+ (byte) 0xe8, (byte) 0x19, (byte) 0xa0, (byte) 0xc5, (byte) 0x05, (byte) 0x75,
+ (byte) 0x1e, (byte) 0x60, (byte) 0x3c, (byte) 0xb8, (byte) 0xf8, (byte) 0xc4,
+ (byte) 0xfe, (byte) 0x98, (byte) 0x71, (byte) 0x91, (byte) 0x85, (byte) 0x56,
+ (byte) 0x27, (byte) 0x94, (byte) 0xa1, (byte) 0x85, (byte) 0xe5, (byte) 0xde,
+ (byte) 0xc4, (byte) 0x15, (byte) 0xc8, (byte) 0x1f, (byte) 0x2f, (byte) 0x16,
+ (byte) 0x2c, (byte) 0xdc, (byte) 0xd6, (byte) 0x50, (byte) 0xdc, (byte) 0xe7,
+ (byte) 0x19, (byte) 0x87, (byte) 0x28, (byte) 0xbf, (byte) 0xc1, (byte) 0xb5,
+ (byte) 0xf9, (byte) 0x49, (byte) 0xb9, (byte) 0xb5, (byte) 0x37, (byte) 0x41,
+ (byte) 0x99, (byte) 0xc6,
+ };
+
+ /*
* Test key generation:
* openssl rand -hex 16
* echo 'ceaa31952dfd3d0f5af4b2042ba06094' | sed 's/\(..\)/(byte) 0x\1, /g'
@@ -2491,17 +2772,20 @@ public final class CipherTest extends TestCase {
public final byte[] iv;
+ public final byte[] aad;
+
public final byte[] plaintext;
public final byte[] ciphertext;
public final byte[] plaintextPadded;
- public CipherTestParam(String transformation, byte[] key, byte[] iv, byte[] plaintext,
- byte[] plaintextPadded, byte[] ciphertext) {
- this.transformation = transformation;
+ public CipherTestParam(String transformation, byte[] key, byte[] iv, byte[] aad,
+ byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext) {
+ this.transformation = transformation.toUpperCase(Locale.ROOT);
this.key = key;
this.iv = iv;
+ this.aad = aad;
this.plaintext = plaintext;
this.plaintextPadded = plaintextPadded;
this.ciphertext = ciphertext;
@@ -2512,23 +2796,34 @@ public final class CipherTest extends TestCase {
static {
CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS5Padding", AES_128_KEY,
null,
+ null,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
// PKCS#5 is assumed to be equivalent to PKCS#7 -- same test vectors are thus used for both.
CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS7Padding", AES_128_KEY,
null,
+ null,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/GCM/NOPADDING",
+ AES_128_GCM_TestVector_1_Key,
+ AES_128_GCM_TestVector_1_IV,
+ AES_128_GCM_TestVector_1_AAD,
+ AES_128_GCM_TestVector_1_Plaintext,
+ AES_128_GCM_TestVector_1_Plaintext,
+ AES_128_GCM_TestVector_1_Encrypted));
if (IS_UNLIMITED) {
CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS5Padding", AES_256_KEY,
AES_256_CBC_PKCS5Padding_TestVector_1_IV,
+ null,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded,
AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext));
CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS7Padding", AES_256_KEY,
AES_256_CBC_PKCS5Padding_TestVector_1_IV,
+ null,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded,
AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext));
@@ -2564,62 +2859,98 @@ public final class CipherTest extends TestCase {
private void checkCipher(CipherTestParam p, String provider) throws Exception {
SecretKey key = new SecretKeySpec(p.key, "AES");
Cipher c = Cipher.getInstance(p.transformation, provider);
+
AlgorithmParameterSpec spec = null;
if (p.iv != null) {
- spec = new IvParameterSpec(p.iv);
+ if (isAEAD(p.transformation)) {
+ spec = new GCMParameterSpec((p.ciphertext.length - p.plaintext.length) * 8, p.iv);
+ } else {
+ spec = new IvParameterSpec(p.iv);
+ }
}
+
c.init(Cipher.ENCRYPT_MODE, key, spec);
+ if (p.aad != null) {
+ c.updateAAD(p.aad);
+ }
final byte[] actualCiphertext = c.doFinal(p.plaintext);
- assertEquals(Arrays.toString(p.ciphertext), Arrays.toString(actualCiphertext));
+ assertEquals(p.transformation + " " + provider, Arrays.toString(p.ciphertext),
+ Arrays.toString(actualCiphertext));
+ c = Cipher.getInstance(p.transformation, provider);
+ c.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] emptyCipherText = c.doFinal();
assertNotNull(emptyCipherText);
c.init(Cipher.DECRYPT_MODE, key, spec);
- try {
- c.updateAAD(new byte[8]);
- fail("Cipher should not support AAD");
- } catch (UnsupportedOperationException expected) {
+ if (!isAEAD(p.transformation)) {
+ try {
+ c.updateAAD(new byte[8]);
+ fail("Cipher should not support AAD");
+ } catch (UnsupportedOperationException | IllegalStateException expected) {
+ }
}
- byte[] emptyPlainText = c.doFinal(emptyCipherText);
- assertEquals(Arrays.toString(new byte[0]), Arrays.toString(emptyPlainText));
+ try {
+ byte[] emptyPlainText = c.doFinal(emptyCipherText);
+ assertEquals(Arrays.toString(new byte[0]), Arrays.toString(emptyPlainText));
+ } catch (AEADBadTagException e) {
+ if (!"AndroidOpenSSL".equals(provider) || !isAEAD(p.transformation)) {
+ throw e;
+ }
+ }
// empty decrypt
{
- if (StandardNames.IS_RI) {
+ if (!isAEAD(p.transformation)
+ && (StandardNames.IS_RI || provider.equals("AndroidOpenSSL"))) {
assertEquals(Arrays.toString(new byte[0]),
Arrays.toString(c.doFinal()));
c.update(new byte[0]);
assertEquals(Arrays.toString(new byte[0]),
Arrays.toString(c.doFinal()));
- } else if (provider.equals("BC")) {
+ } else if (provider.equals("BC") || isAEAD(p.transformation)) {
try {
c.doFinal();
fail();
- } catch (IllegalBlockSizeException expected) {
+ } catch (IllegalBlockSizeException maybe) {
+ if (isAEAD(p.transformation)) {
+ throw maybe;
+ }
+ } catch (AEADBadTagException maybe) {
+ if (!isAEAD(p.transformation)) {
+ throw maybe;
+ }
}
try {
c.update(new byte[0]);
c.doFinal();
fail();
- } catch (IllegalBlockSizeException expected) {
+ } catch (IllegalBlockSizeException maybe) {
+ if (isAEAD(p.transformation)) {
+ throw maybe;
+ }
+ } catch (AEADBadTagException maybe) {
+ if (!isAEAD(p.transformation)) {
+ throw maybe;
+ }
}
- } else if (provider.equals("AndroidOpenSSL")) {
- assertNull(c.doFinal());
-
- c.update(new byte[0]);
- assertNull(c.doFinal());
} else {
throw new AssertionError("Define your behavior here for " + provider);
}
}
+ // Cipher might be in unspecified state from failures above.
+ c.init(Cipher.DECRYPT_MODE, key, spec);
+
// .doFinal(input)
{
+ if (p.aad != null) {
+ c.updateAAD(p.aad);
+ }
final byte[] actualPlaintext = c.doFinal(p.ciphertext);
assertEquals(Arrays.toString(p.plaintext), Arrays.toString(actualPlaintext));
}
@@ -2629,6 +2960,12 @@ public final class CipherTest extends TestCase {
final byte[] largerThanCiphertext = new byte[p.ciphertext.length + 5];
System.arraycopy(p.ciphertext, 0, largerThanCiphertext, 5, p.ciphertext.length);
+ if (p.aad != null) {
+ final byte[] largerThanAad = new byte[p.aad.length + 100];
+ System.arraycopy(p.aad, 0, largerThanAad, 50, p.aad.length);
+ c.updateAAD(largerThanAad, 50, p.aad.length);
+ }
+
final byte[] actualPlaintext = new byte[c.getOutputSize(p.ciphertext.length)];
assertEquals(p.plaintext.length,
c.doFinal(largerThanCiphertext, 5, p.ciphertext.length, actualPlaintext));
@@ -2641,6 +2978,12 @@ public final class CipherTest extends TestCase {
final byte[] largerThanCiphertext = new byte[p.ciphertext.length + 10];
System.arraycopy(p.ciphertext, 0, largerThanCiphertext, 5, p.ciphertext.length);
+ if (p.aad != null) {
+ final byte[] largerThanAad = new byte[p.aad.length + 2];
+ System.arraycopy(p.aad, 0, largerThanAad, 2, p.aad.length);
+ c.updateAAD(largerThanAad, 2, p.aad.length);
+ }
+
final byte[] actualPlaintext = new byte[c.getOutputSize(p.ciphertext.length) + 2];
assertEquals(p.plaintext.length,
c.doFinal(largerThanCiphertext, 5, p.ciphertext.length, actualPlaintext, 1));
@@ -2648,13 +2991,18 @@ public final class CipherTest extends TestCase {
Arrays.toString(Arrays.copyOfRange(actualPlaintext, 1, p.plaintext.length + 1)));
}
- Cipher cNoPad = Cipher.getInstance(
- getCipherTransformationWithNoPadding(p.transformation), provider);
- cNoPad.init(Cipher.DECRYPT_MODE, key, spec);
+ if (!p.transformation.endsWith("NOPADDING")) {
+ Cipher cNoPad = Cipher.getInstance(
+ getCipherTransformationWithNoPadding(p.transformation), provider);
+ cNoPad.init(Cipher.DECRYPT_MODE, key, spec);
- final byte[] actualPlaintextPadded = cNoPad.doFinal(p.ciphertext);
- assertEquals(provider + ":" + cNoPad.getAlgorithm(), Arrays.toString(p.plaintextPadded),
- Arrays.toString(actualPlaintextPadded));
+ if (p.aad != null) {
+ c.updateAAD(p.aad);
+ }
+ final byte[] actualPlaintextPadded = cNoPad.doFinal(p.ciphertext);
+ assertEquals(provider + ":" + cNoPad.getAlgorithm(),
+ Arrays.toString(p.plaintextPadded), Arrays.toString(actualPlaintextPadded));
+ }
// Test wrapping a key. Every cipher should be able to wrap.
{
@@ -2664,6 +3012,7 @@ public final class CipherTest extends TestCase {
SecretKey sk = kg.generateKey();
// Wrap it
+ c = Cipher.getInstance(p.transformation, provider);
c.init(Cipher.WRAP_MODE, key, spec);
byte[] cipherText = c.wrap(sk);
@@ -2770,6 +3119,27 @@ public final class CipherTest extends TestCase {
fail("should not be able to call updateAAD with too large length");
} catch (IllegalArgumentException expected) {
}
+
+ try {
+ c.updateAAD(new byte[8]);
+ fail("should not be able to call updateAAD on non-AEAD cipher");
+ } catch (UnsupportedOperationException | IllegalStateException expected) {
+ }
+ }
+
+ public void testCipher_updateAAD_AfterInit_WithGcm_Success() throws Exception {
+ Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
+ c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[128 / 8], "AES"));
+ c.updateAAD(new byte[8]);
+ c.updateAAD(new byte[8]);
+ }
+
+ public void testCipher_updateAAD_AfterUpdate_WithGcm_Sucess() throws Exception {
+ Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
+ c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[128 / 8], "AES"));
+ c.updateAAD(new byte[8]);
+ c.update(new byte[8]);
+ c.updateAAD(new byte[8]);
}
public void testCipher_ShortBlock_Failure() throws Exception {
@@ -2797,7 +3167,24 @@ public final class CipherTest extends TestCase {
}
}
+ public void testCipher_Update_WithZeroLengthInput_ReturnsNull() throws Exception {
+ SecretKey key = new SecretKeySpec(AES_128_KEY, "AES");
+ Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
+ c.init(Cipher.ENCRYPT_MODE, key);
+ assertNull(c.update(new byte[0]));
+ assertNull(c.update(new byte[c.getBlockSize() * 2], 0, 0));
+
+ // Try with non-zero offset just in case the implementation mixes up offset and inputLen
+ assertNull(c.update(new byte[c.getBlockSize() * 2], 16, 0));
+ }
+
private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) throws Exception {
+ // Do not try to test ciphers with no padding already.
+ String noPaddingTransform = getCipherTransformationWithNoPadding(p.transformation);
+ if (p.transformation.equals(noPaddingTransform)) {
+ return;
+ }
+
SecretKey key = new SecretKeySpec(p.key, "AES");
Cipher c = Cipher.getInstance(
getCipherTransformationWithNoPadding(p.transformation), provider);
@@ -2805,12 +3192,14 @@ public final class CipherTest extends TestCase {
return;
}
- c.init(Cipher.ENCRYPT_MODE, key);
- try {
- c.doFinal(new byte[] { 0x01, 0x02, 0x03 });
- fail("Should throw IllegalBlockSizeException on wrong-sized block; provider="
- + provider);
- } catch (IllegalBlockSizeException expected) {
+ if (!p.transformation.endsWith("NOPADDING")) {
+ c.init(Cipher.ENCRYPT_MODE, key);
+ try {
+ c.doFinal(new byte[] { 0x01, 0x02, 0x03 });
+ fail("Should throw IllegalBlockSizeException on wrong-sized block; transform="
+ + p.transformation + " provider=" + provider);
+ } catch (IllegalBlockSizeException expected) {
+ }
}
}
@@ -2985,4 +3374,145 @@ public final class CipherTest extends TestCase {
}
}
}
+
+ /**
+ * Several exceptions can be thrown by init. Check that in this case we throw the right one,
+ * as the error could fall under the umbrella of other exceptions.
+ * http://b/18987633
+ */
+ public void testCipher_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", "none");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Cipher c = Cipher.getInstance("FOO");
+ c.init(Cipher.DECRYPT_MODE, new MockKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
+ /*
+ * When in decrypt mode and using padding, the buffer shouldn't necessarily have room for an
+ * extra block when using padding.
+ * http://b/19186852
+ */
+ public void testDecryptBufferMultipleBlockSize_mustNotThrowException() throws Exception {
+ String testString = "Hello, World!";
+ byte[] testKey = "0123456789012345".getBytes(StandardCharsets.US_ASCII);
+ String testedCipher = "AES/ECB/PKCS7Padding";
+
+ Cipher encCipher = Cipher.getInstance(testedCipher);
+ encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(testKey, "AES"));
+ byte[] plainBuffer = testString.getBytes(StandardCharsets.US_ASCII);
+ byte[] encryptedBuffer = new byte[16];
+ int encryptedLength = encCipher.doFinal(
+ plainBuffer, 0, plainBuffer.length, encryptedBuffer);
+ assertEquals(16, encryptedLength);
+
+ Cipher cipher = Cipher.getInstance(testedCipher);
+ cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(testKey, "AES"));
+ // Must not throw exception.
+ int unencryptedBytes = cipher.doFinal(
+ encryptedBuffer, 0, encryptedBuffer.length, encryptedBuffer);
+ assertEquals(testString,
+ new String(encryptedBuffer, 0, unencryptedBytes, StandardCharsets.US_ASCII));
+ }
+
+ /**
+ * When using padding in decrypt mode, ensure that empty buffers decode to empty strings
+ * (no padding needed for the empty buffer).
+ * http://b/19186852
+ */
+ public void testDecryptBufferZeroSize_mustDecodeToEmptyString() throws Exception {
+ String[] androidOpenSSLCiphers = { "AES/CBC/PKCS5PADDING", "AES/CBC/PKCS7PADDING",
+ "AES/ECB/PKCS5PADDING", "AES/ECB/PKCS7PADDING", "DESEDE/CBC/PKCS5PADDING",
+ "DESEDE/CBC/PKCS7PADDING" };
+ for (String c : androidOpenSSLCiphers) {
+ Cipher cipher = Cipher.getInstance(c);
+ if (c.contains("/CBC/")) {
+ cipher.init(Cipher.DECRYPT_MODE,
+ new SecretKeySpec("0123456789012345".getBytes(StandardCharsets.US_ASCII),
+ (c.startsWith("AES/")) ? "AES" : "DESEDE"),
+ new IvParameterSpec(
+ ("01234567" + ((c.startsWith("AES/")) ? "89012345" : ""))
+ .getBytes(StandardCharsets.US_ASCII)));
+ } else {
+ cipher.init(Cipher.DECRYPT_MODE,
+ new SecretKeySpec("0123456789012345".getBytes(StandardCharsets.US_ASCII),
+ (c.startsWith("AES/")) ? "AES" : "DESEDE"));
+ }
+
+ byte[] buffer = new byte[0];
+ int bytesProduced = cipher.doFinal(buffer, 0, buffer.length, buffer);
+ assertEquals("", new String(buffer, 0, bytesProduced, StandardCharsets.US_ASCII));
+ }
+ }
+
+ /**
+ * If a provider rejects a key for "Cipher/Mode/Padding"", there might be another that
+ * accepts the key for "Cipher". Don't throw InvalidKeyException when trying the first one.
+ * http://b/22208820
+ */
+ public void testCipher_init_tryAllCombinationsBeforeThrowingInvalidKey()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO/FOO/FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO/FOO/FOO SupportedKeyClasses", "none");
+ }
+ };
+
+ Provider mockProvider2 = new MockProvider("MockProvider2") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ Security.addProvider(mockProvider);
+
+ try {
+ try {
+ // The provider installed doesn't accept the key.
+ Cipher c = Cipher.getInstance("FOO/FOO/FOO");
+ c.init(Cipher.DECRYPT_MODE, new MockKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ }
+
+ Security.addProvider(mockProvider2);
+
+ try {
+ // The new provider accepts "FOO" with this key. Use it despite the other provider
+ // accepts "FOO/FOO/FOO" but doesn't accept the key.
+ Cipher c = Cipher.getInstance("FOO/FOO/FOO");
+ c.init(Cipher.DECRYPT_MODE, new MockKey());
+ assertEquals("MockProvider2", c.getProvider().getName());
+ } finally {
+ Security.removeProvider(mockProvider2.getName());
+ }
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
+ /**
+ * Check that RSA with OAEPPadding is supported.
+ * http://b/22208820
+ */
+ public void test_RSA_OAEPPadding() throws Exception {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(1024, SecureRandom.getInstance("SHA1PRNG"));
+ Cipher cipher = Cipher.getInstance("RSA/NONE/OAEPPadding");
+ cipher.init(Cipher.ENCRYPT_MODE, keyGen.generateKeyPair().getPublic());
+ cipher.doFinal(new byte[] {1,2,3,4});
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/ECDHKeyAgreementTest.java b/luni/src/test/java/libcore/javax/crypto/ECDHKeyAgreementTest.java
index cabe5c9..cc29640 100644
--- a/luni/src/test/java/libcore/javax/crypto/ECDHKeyAgreementTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/ECDHKeyAgreementTest.java
@@ -38,7 +38,9 @@ import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Comparator;
+import java.util.List;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
@@ -374,8 +376,8 @@ public class ECDHKeyAgreementTest extends TestCase {
if (providers == null) {
return new Provider[0];
}
- // Sort providers by name to guarantee non-determinism in the order in which providers are
- // used in the tests.
+ // Sort providers by name to guarantee deterministic order in which providers are used in
+ // the tests.
return sortByName(providers);
}
@@ -384,8 +386,21 @@ public class ECDHKeyAgreementTest extends TestCase {
if (providers == null) {
return new Provider[0];
}
- // Sort providers by name to guarantee non-determinism in the order in which providers are
- // used in the tests.
+
+ // Do not test AndroidKeyStore's KeyFactory. It only handles Android Keystore-backed keys.
+ // It's OKish not to test AndroidKeyStore's KeyFactory here because it's tested by
+ // cts/tests/test/keystore.
+ List<Provider> filteredProvidersList = new ArrayList<Provider>(providers.length);
+ for (Provider provider : providers) {
+ if ("AndroidKeyStore".equals(provider.getName())) {
+ continue;
+ }
+ filteredProvidersList.add(provider);
+ }
+ providers = filteredProvidersList.toArray(new Provider[filteredProvidersList.size()]);
+
+ // Sort providers by name to guarantee deterministic order in which providers are used in
+ // the tests.
return sortByName(providers);
}
diff --git a/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
new file mode 100644
index 0000000..9281b43
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 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 libcore.javax.crypto;
+
+import java.security.InvalidKeyException;
+import java.security.Provider;
+import java.security.Security;
+
+import javax.crypto.KeyAgreement;
+
+import junit.framework.TestCase;
+
+public class KeyAgreementTest extends TestCase {
+ private static abstract class MockProvider extends Provider {
+ public MockProvider(String name) {
+ super(name, 1.0, "Mock provider used for testing");
+ setup();
+ }
+
+ public abstract void setup();
+ }
+
+ public void testKeyAgreement_getInstance_SuppliedProviderNotRegistered_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ {
+ KeyAgreement c = KeyAgreement.getInstance("FOO", mockProvider);
+ c.init(new MockKey());
+ assertEquals(mockProvider, c.getProvider());
+ }
+ }
+
+ public void testKeyAgreement_getInstance_DoesNotSupportKeyClass_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ put("KeyAgreement.FOO SupportedKeyClasses", "none");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ KeyAgreement c = KeyAgreement.getInstance("FOO", mockProvider);
+ c.init(new MockKey());
+ assertEquals(mockProvider, c.getProvider());
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
+ /**
+ * Several exceptions can be thrown by init. Check that in this case we throw the right one,
+ * as the error could fall under the umbrella of other exceptions.
+ * http://b/18987633
+ */
+ public void testKeyAgreement_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ put("KeyAgreement.FOO SupportedKeyClasses", "none");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ KeyAgreement c = KeyAgreement.getInstance("FOO");
+ c.init(new MockKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java b/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
index 8bbd548..5763562 100644
--- a/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
@@ -44,6 +44,15 @@ public class KeyGeneratorTest extends TestCase {
if (!type.equals("KeyGenerator")) {
continue;
}
+
+ // Do not test AndroidKeyStore's KeyGenerator. It cannot be initialized without
+ // providing AndroidKeyStore-specific algorithm parameters.
+ // It's OKish not to test AndroidKeyStore's KeyGenerator here because it's tested
+ // by cts/tests/test/keystore.
+ if ("AndroidKeyStore".equals(provider.getName())) {
+ continue;
+ }
+
String algorithm = service.getAlgorithm();
try {
// KeyGenerator.getInstance(String)
diff --git a/luni/src/test/java/libcore/javax/crypto/MacTest.java b/luni/src/test/java/libcore/javax/crypto/MacTest.java
new file mode 100644
index 0000000..314a564
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MacTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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 libcore.javax.crypto;
+
+import junit.framework.TestCase;
+
+import java.security.InvalidKeyException;
+import java.security.Provider;
+import java.security.Security;
+
+import javax.crypto.Mac;
+
+public class MacTest extends TestCase {
+ private static abstract class MockProvider extends Provider {
+ public MockProvider(String name) {
+ super(name, 1.0, "Mock provider used for testing");
+ setup();
+ }
+
+ public abstract void setup();
+ }
+
+ /**
+ * Several exceptions can be thrown by init. Check that in this case we throw the right one,
+ * as the error could fall under the umbrella of other exceptions.
+ * http://b/18987633
+ */
+ public void testMac_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+ put("Mac.FOO SupportedKeyClasses", "none");
+
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Mac c = Mac.getInstance("FOO");
+ c.init(new MockKey());
+ fail("Expected InvalidKeyException");
+ } catch (InvalidKeyException expected) {
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java b/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
index 6742cf3..c1b1bd2 100644
--- a/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
+++ b/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
@@ -25,6 +25,7 @@ import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
@@ -56,6 +57,92 @@ public class MockCipherSpi extends CipherSpi {
public static class AllKeyTypes extends MockCipherSpi {
}
+ public static class MustInitWithAlgorithmParameterSpec_RejectsAll extends MockCipherSpi {
+ @Override
+ protected void engineInit(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException {
+ throw new AssertionError("Must have AlgorithmParameterSpec");
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ throw new InvalidAlgorithmParameterException("expected rejection");
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameters params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ throw new AssertionError("Must have AlgorithmParameterSpec");
+ }
+ }
+
+ public static class MustInitWithAlgorithmParameters_RejectsAll extends MockCipherSpi {
+ @Override
+ protected void engineInit(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException {
+ throw new AssertionError("Must have AlgorithmParameterSpec");
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ throw new AssertionError("Must have AlgorithmParameterSpec");
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameters params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ throw new InvalidAlgorithmParameterException("expected rejection");
+ }
+ }
+
+ public static class MustInitWithAlgorithmParameters_ThrowsNull extends MockCipherSpi {
+ @Override
+ protected void engineInit(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException {
+ throw new NullPointerException("expected rejection");
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ throw new NullPointerException("expected rejection");
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameters params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ throw new NullPointerException("expected rejection");
+ }
+ }
+
+ public static class MustInitForEncryptModeOrRejects extends MockCipherSpi {
+ @Override
+ protected void engineInit(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException {
+ if (opmode != Cipher.ENCRYPT_MODE) {
+ throw new InvalidKeyException("expected rejection");
+ }
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (opmode != Cipher.ENCRYPT_MODE) {
+ throw new InvalidKeyException("expected rejection");
+ }
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameters params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (opmode != Cipher.ENCRYPT_MODE) {
+ throw new InvalidKeyException("expected rejection");
+ }
+ }
+ }
+
public void checkKeyType(Key key) throws InvalidKeyException {
}
diff --git a/luni/src/test/java/libcore/javax/crypto/MockKey.java b/luni/src/test/java/libcore/javax/crypto/MockKey.java
index 248e2de..1c758f3 100644
--- a/luni/src/test/java/libcore/javax/crypto/MockKey.java
+++ b/luni/src/test/java/libcore/javax/crypto/MockKey.java
@@ -25,7 +25,7 @@ import java.security.Key;
public class MockKey implements Key {
@Override
public String getAlgorithm() {
- throw new UnsupportedOperationException("not implemented");
+ return "MOCK";
}
@Override
diff --git a/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java b/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java
new file mode 100644
index 0000000..574dbeb
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015 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 libcore.javax.crypto;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+
+/**
+ * Mock KeyAgreementSpi used by {@link KeyAgreementTest}.
+ */
+public class MockKeyAgreementSpi extends KeyAgreementSpi {
+ public static class SpecificKeyTypes extends MockKeyAgreementSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockKey)) {
+ throw new InvalidKeyException("Must be MockKey!");
+ }
+ }
+ }
+
+ public static class SpecificKeyTypes2 extends MockKeyAgreementSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ System.err.println("Checking key of type " + key.getClass().getName());
+ if (!(key instanceof MockKey2)) {
+ throw new InvalidKeyException("Must be MockKey2!");
+ }
+ }
+ }
+
+ public static class AllKeyTypes extends MockKeyAgreementSpi {
+ }
+
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ }
+
+ @Override
+ protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException,
+ IllegalStateException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected byte[] engineGenerateSecret() throws IllegalStateException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+ throws IllegalStateException, ShortBufferException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException,
+ NoSuchAlgorithmException, InvalidKeyException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+ checkKeyType(key);
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ checkKeyType(key);
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java b/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java
new file mode 100644
index 0000000..0edeba7
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MockMacSpi.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 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 libcore.javax.crypto;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.MacSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+
+/**
+ * Mock CipherSpi used by {@link libcore.javax.crypto.CipherTest}.
+ */
+public class MockMacSpi extends MacSpi {
+ public static class SpecificKeyTypes extends MockMacSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockKey)) {
+ throw new InvalidKeyException("Must be MockKey!");
+ }
+ }
+ }
+
+ public static class SpecificKeyTypes2 extends MockMacSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ System.err.println("Checking key of type " + key.getClass().getName());
+ if (!(key instanceof MockKey2)) {
+ throw new InvalidKeyException("Must be MockKey2!");
+ }
+ }
+ }
+
+ public static class AllKeyTypes extends MockMacSpi {
+ }
+
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ }
+
+ @Override
+ protected int engineGetMacLength() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int inputOffset, int inputLen) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected byte[] engineDoFinal() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineReset() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
index feecebe..07ecd12 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
@@ -22,6 +22,7 @@ import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.security.PublicKey;
+import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -30,21 +31,32 @@ import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
-import javax.net.ssl.DefaultHostnameVerifier;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
import javax.security.auth.x500.X500Principal;
import junit.framework.TestCase;
+/**
+ * Tests for the platform-default {@link HostnameVerifier} as provided by
+ * {@link HttpsURLConnection#getDefaultHostnameVerifier()}.
+ */
public final class DefaultHostnameVerifierTest extends TestCase {
private static final int ALT_UNKNOWN = 0;
private static final int ALT_DNS_NAME = 2;
private static final int ALT_IPA_NAME = 7;
- private final DefaultHostnameVerifier verifier = new DefaultHostnameVerifier();
+ private final HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
public void testVerify() {
- assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")));
- assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
- assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
+ assertTrue(verifyWithServerCertificate(
+ "imap.g.com", new StubX509Certificate("cn=imap.g.com")));
+ assertFalse(verifyWithServerCertificate(
+ "imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
+ assertFalse(verifyWithServerCertificate(
+ "imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
}
/**
@@ -52,32 +64,33 @@ public final class DefaultHostnameVerifierTest extends TestCase {
* be used as the identity and the CN should be ignored.
*/
public void testSubjectAltNameAndCn() {
- assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("")
- .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
- assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")
+ assertFalse(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
- assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+ assertFalse(
+ verifyWithServerCertificate("imap.g.com", new StubX509Certificate("cn=imap.g.com")
+ .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
+ assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")));
}
public void testSubjectAltNameWithWildcard() {
- assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")));
}
public void testSubjectAltNameWithIpAddress() {
- assertTrue(verifier.verify("1.2.3.4", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
- assertFalse(verifier.verify("1.2.3.5", new StubX509Certificate("")
+ assertFalse(verifyWithServerCertificate("1.2.3.5", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
- assertTrue(verifier.verify("192.168.100.1", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("192.168.100.1", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
.addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1")));
}
public void testUnknownSubjectAltName() {
// Has unknown subject alternative names
- assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -85,7 +98,7 @@ public final class DefaultHostnameVerifierTest extends TestCase {
.addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
.addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
- assertTrue(verifier.verify("2.33.44.55", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("2.33.44.55", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -93,7 +106,7 @@ public final class DefaultHostnameVerifierTest extends TestCase {
.addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
.addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
- assertFalse(verifier.verify("g.com", new StubX509Certificate("")
+ assertFalse(verifyWithServerCertificate("g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -101,7 +114,7 @@ public final class DefaultHostnameVerifierTest extends TestCase {
.addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
.addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
- assertFalse(verifier.verify("2.33.44.1", new StubX509Certificate("")
+ assertFalse(verifyWithServerCertificate("2.33.44.1", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -111,40 +124,129 @@ public final class DefaultHostnameVerifierTest extends TestCase {
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
}
- public void testWildcardMatchesWildcardSuffix() {
- assertTrue(verifier.verifyHostName("b.c.d", "*.b.c.d"));
- assertTrue(verifier.verifyHostName("imap.google.com", "*.imap.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com.au", "*.imap.google.com"));
+ public void testWildcardsRejectedForIpAddress() {
+ assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4")));
+ assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4")
+ .addSubjectAlternativeName(ALT_IPA_NAME, "*.2.3.4")
+ .addSubjectAlternativeName(ALT_DNS_NAME, "*.2.3.4")));
+ assertFalse(verifyWithServerCertificate(
+ "2001:1234::1", new StubX509Certificate("cn=*:1234::1")));
+ assertFalse(verifyWithServerCertificate(
+ "2001:1234::1", new StubX509Certificate("cn=*:1234::1")
+ .addSubjectAlternativeName(ALT_IPA_NAME, "*:1234::1")
+ .addSubjectAlternativeName(ALT_DNS_NAME, "*:1234::1")));
+ }
+
+ public void testNullParameters() {
+ // Confirm that neither of the parameters used later in the test cause the verifier to blow
+ // up
+ String hostname = "www.example.com";
+ StubSSLSession session = new StubSSLSession();
+ session.peerCertificates =
+ new Certificate[] {new StubX509Certificate("cn=www.example.com")};
+ verifier.verify(hostname, session);
+
+ try {
+ verifier.verify(hostname, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ verifier.verify(null, session);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testInvalidDomainNames() {
+ assertFalse(verifyWithDomainNamePattern("", ""));
+ assertFalse(verifyWithDomainNamePattern(".test.example.com", ".test.example.com"));
+ assertFalse(verifyWithDomainNamePattern("ex*ample.com", "ex*ample.com"));
+ assertFalse(verifyWithDomainNamePattern("example.com..", "example.com."));
+ assertFalse(verifyWithDomainNamePattern("example.com.", "example.com.."));
+ }
+
+ public void testWildcardCharacterMustBeLeftMostLabelOnly() {
+ assertFalse(verifyWithDomainNamePattern("test.www.example.com", "test.*.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "www.*.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "www.example.*"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "*www.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "*w.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "w*w.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "w*.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "www*.example.com"));
}
- public void testWildcardMatchingSubstring() {
- assertTrue(verifier.verifyHostName("b.c.d", "b*.c.d"));
- assertTrue(verifier.verifyHostName("imap.google.com", "ima*.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com.au", "ima*.google.com"));
+ public void testWildcardCannotMatchEmptyLabel() {
+ assertFalse(verifyWithDomainNamePattern("example.com", "*.example.com"));
+ assertFalse(verifyWithDomainNamePattern(".example.com", "*.example.com"));
}
- public void testWildcardMatchingEmptySubstring() {
- assertTrue(verifier.verifyHostName("imap.google.com", "imap*.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com.au", "imap*.google.com"));
+ public void testWildcardCannotMatchChildDomain() {
+ assertFalse(verifyWithDomainNamePattern("sub.www.example.com", "*.example.com"));
}
- public void testWildcardMatchesChildDomain() {
- assertFalse(verifier.verifyHostName("a.b.c.d", "*.c.d"));
+ public void testWildcardRejectedForSingleLabelPatterns() {
+ assertFalse(verifyWithDomainNamePattern("d", "*"));
+ assertFalse(verifyWithDomainNamePattern("d.", "*."));
+ assertFalse(verifyWithDomainNamePattern("d", "d*"));
+ assertFalse(verifyWithDomainNamePattern("d.", "d*."));
+ assertFalse(verifyWithDomainNamePattern("d", "*d"));
+ assertFalse(verifyWithDomainNamePattern("d.", "*d."));
+ assertFalse(verifyWithDomainNamePattern("ddd", "d*d"));
+ assertFalse(verifyWithDomainNamePattern("ddd.", "d*d."));
+ }
+
+ public void testNoPrefixMatch() {
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.au", "imap.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.au", "*.google.com"));
}
public void testVerifyHostName() {
- assertTrue(verifier.verifyHostName("a.b.c.d", "a.b.c.d"));
- assertTrue(verifier.verifyHostName("a.b.c.d", "*.b.c.d"));
- assertFalse(verifier.verifyHostName("a.b.c.d", "*.*.c.d"));
- assertTrue(verifier.verifyHostName("imap.google.com", "imap.google.com"));
- assertFalse(verifier.verifyHostName("imap2.google.com", "imap.google.com"));
- assertTrue(verifier.verifyHostName("imap.google.com", "*.google.com"));
- assertTrue(verifier.verifyHostName("imap2.google.com", "*.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com", "*.googl.com"));
- assertFalse(verifier.verifyHostName("imap2.google2.com", "*.google3.com"));
- assertFalse(verifier.verifyHostName("imap.google.com", "a*.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com", "ix*.google.com"));
- assertTrue(verifier.verifyHostName("imap.google.com", "iMap.Google.Com"));
+ assertTrue(verifyWithDomainNamePattern("a.b.c.d", "a.b.c.d"));
+ assertTrue(verifyWithDomainNamePattern("a.b.c.d", "*.b.c.d"));
+ assertFalse(verifyWithDomainNamePattern("a.b.c.d", "*.*.c.d"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com", "imap.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap2.google.com", "imap.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com", "*.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap2.google.com", "*.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com", "*.googl.com"));
+ assertFalse(verifyWithDomainNamePattern("imap2.google2.com", "*.google3.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com", "a*.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com", "ix*.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com", "iMap.Google.Com"));
+ assertTrue(verifyWithDomainNamePattern("weird", "weird"));
+ assertTrue(verifyWithDomainNamePattern("weird", "weird."));
+
+ // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
+ assertFalse(verifyWithDomainNamePattern("weird", "weird*"));
+ assertFalse(verifyWithDomainNamePattern("weird", "*weird"));
+ assertFalse(verifyWithDomainNamePattern("weird", "weird*."));
+ assertFalse(verifyWithDomainNamePattern("weird", "weird.*"));
+ }
+
+ public void testVerifyAbsoluteHostName() {
+ assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "a.b.c.d"));
+ assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "*.b.c.d"));
+ assertFalse(verifyWithDomainNamePattern("a.b.c.d.", "*.*.c.d"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com.", "imap.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap2.google.com.", "imap.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com.", "*.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap2.google.com.", "*.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.", "*.googl.com"));
+ assertFalse(verifyWithDomainNamePattern("imap2.google2.com.", "*.google3.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.", "a*.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.", "ix*.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com.", "iMap.Google.Com"));
+ assertTrue(verifyWithDomainNamePattern("weird.", "weird"));
+ assertTrue(verifyWithDomainNamePattern("weird.", "weird."));
+
+ // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
+ assertFalse(verifyWithDomainNamePattern("weird.", "*weird"));
+ assertFalse(verifyWithDomainNamePattern("weird.", "weird*"));
+ assertFalse(verifyWithDomainNamePattern("weird.", "weird*."));
+ assertFalse(verifyWithDomainNamePattern("weird.", "weird.*"));
}
public void testSubjectOnlyCert() throws Exception {
@@ -168,8 +270,8 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "rs2oQLwOLnuifH52ey9+tJguabo+brlYYigAuWWFEzJfBzikDkIwnE/L7wlrypIk\n"
+ "taXDWI4=\n"
+ "-----END CERTIFICATE-----");
- assertTrue(verifier.verify("www.example.com", cert));
- assertFalse(verifier.verify("www2.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www2.example.com", cert));
}
public void testSubjectAltOnlyCert() throws Exception {
@@ -192,8 +294,8 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "JPRynf9244Pn0Sr/wsnmdsTRFIFYynrc51hQ7DkwbUxpcaewkZzilru/SwZ3+pPT\n"
+ "9JSqm5hJ1pg5WDlPkW7c/1VA0/141N52Q8MIU+2ZpuOj\n"
+ "-----END CERTIFICATE-----");
- assertTrue(verifier.verify("www.example.com", cert));
- assertFalse(verifier.verify("www2.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www2.example.com", cert));
}
public void testSubjectWithAltNamesCert() throws Exception {
@@ -219,10 +321,10 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "hrTVypLSoRXuTB2aWilu4p6aNh84xTdyqo2avtNr2MiQMZIcdamBq8LdBIAShFXI\n"
+ "h5G2eVGXH/Y=\n"
+ "-----END CERTIFICATE-----");
- assertFalse(verifier.verify("www.example.com", cert));
- assertTrue(verifier.verify("www2.example.com", cert));
- assertTrue(verifier.verify("www3.example.com", cert));
- assertFalse(verifier.verify("www4.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www2.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www3.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www4.example.com", cert));
}
public void testSubjectWithWildAltNamesCert() throws Exception {
@@ -247,11 +349,11 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "Y3R0HZvKzNIU3pwAm69HCJoG+/9MZEIDJb0WJc5UygxDT45XE9zQMQe4dBOTaNXT\n"
+ "+ntgaB62kE10HzrzpqXAgoAWxWK4RzFcUpBWw9qYq9xOCewJ\n"
+ "-----END CERTIFICATE-----");
- assertFalse(verifier.verify("www.example.com", cert));
- assertFalse(verifier.verify("www2.example.com", cert));
- assertTrue(verifier.verify("www.example2.com", cert));
- assertTrue(verifier.verify("abc.example2.com", cert));
- assertFalse(verifier.verify("www.example3.com", cert));
+ assertFalse(verifyWithServerCertificate("www.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www2.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www.example2.com", cert));
+ assertTrue(verifyWithServerCertificate("abc.example2.com", cert));
+ assertFalse(verifyWithServerCertificate("www.example3.com", cert));
}
public void testWildAltNameOnlyCert() throws Exception {
@@ -274,9 +376,9 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "va++ow5r1VxQXFJc0ZPzsDo+6TlktoDHaRQJGMqQomqHWT4i7F5UZgf6BHGfEUPU\n"
+ "qep+GsF3QRHSBtpObWkVDZNFvky3a1iZ2q25+hFIqQ==\n"
+ "-----END CERTIFICATE-----");
- assertTrue(verifier.verify("www.example.com", cert));
- assertTrue(verifier.verify("www2.example.com", cert));
- assertFalse(verifier.verify("www.example2.com", cert));
+ assertTrue(verifyWithServerCertificate("www.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www2.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www.example2.com", cert));
}
public void testAltIpOnlyCert() throws Exception {
@@ -299,8 +401,48 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "WPjHQcWfpkFzAF5wyOq0kveVfx0g5xPhOVDd+U+q7WastbXICpCoHp9FxISmZVik\n"
+ "sAyifp8agkYdzaSh55fFmKXlFnRsQw==\n"
+ "-----END CERTIFICATE-----");
- assertTrue(verifier.verify("192.168.10.1", cert));
- assertFalse(verifier.verify("192.168.10.2", cert));
+ assertTrue(verifyWithServerCertificate("192.168.10.1", cert));
+ assertFalse(verifyWithServerCertificate("192.168.10.2", cert));
+ }
+
+ /**
+ * Verifies the provided hostname against the provided domain name pattern from server
+ * certificate.
+ */
+ private boolean verifyWithDomainNamePattern(String hostname, String pattern) {
+ StubSSLSession session = new StubSSLSession();
+
+ // Verify using a certificate where the pattern is in the CN
+ session.peerCertificates = new Certificate[] {
+ new StubX509Certificate("cn=\"" + pattern + "\"")
+ };
+ boolean resultWhenPatternInCn = verifier.verify(hostname, session);
+
+ // Verify using a certificate where the pattern is in a DNS SubjectAltName
+ session.peerCertificates = new Certificate[] {
+ new StubX509Certificate("ou=test")
+ .addSubjectAlternativeName(ALT_DNS_NAME, pattern)
+ };
+ boolean resultWhenPatternInSubjectAltName = verifier.verify(hostname, session);
+
+ // Assert that in both cases the verifier gives the same result
+ if (resultWhenPatternInCn != resultWhenPatternInSubjectAltName) {
+ fail("Different results between pattern in CN and SubjectAltName."
+ + " hostname : " + hostname + ", pattern: " + pattern
+ + ", when pattern in CN: " + resultWhenPatternInCn
+ + ", when pattern in SubjectAltName: " + resultWhenPatternInSubjectAltName);
+ }
+ return resultWhenPatternInCn;
+ }
+
+ /**
+ * Verifies the provided hostname against the provided server certificate.
+ */
+ private boolean verifyWithServerCertificate(String hostname, X509Certificate certificate) {
+ StubSSLSession session = new StubSSLSession();
+ session.peerCertificates =
+ (certificate != null) ? new Certificate[] {certificate} : new Certificate[0];
+ return verifier.verify(hostname, session);
}
X509Certificate parseCertificate(String encoded) throws Exception {
@@ -308,6 +450,117 @@ public final class DefaultHostnameVerifierTest extends TestCase {
return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(in);
}
+ private static class StubSSLSession implements SSLSession {
+
+ public Certificate[] peerCertificates = new Certificate[0];
+
+ @Override
+ public int getApplicationBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getCipherSuite() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getCreationTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] getId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ return peerCertificates;
+ }
+
+ @Override
+ public String getPeerHost() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPeerPort() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getProtocol() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getValueNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void invalidate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public void putValue(String name, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
private static class StubX509Certificate extends X509Certificate {
private final X500Principal subjectX500Principal;
private Collection<List<?>> subjectAlternativeNames;
diff --git a/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java b/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java
new file mode 100644
index 0000000..cbaea20
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 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 libcore.javax.net.ssl;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+
+public class HttpsURLConnectionTest extends TestCase {
+
+ /**
+ * HTTPS URL which cannot be resolved and is thus safe to use in tests where network traffic
+ * should be avoided.
+ */
+ private static final String UNRESOLVABLE_HTTPS_URL = "https:///";
+
+ public void testDefaultHostnameVerifierNotNull() {
+ assertNotNull(HttpsURLConnection.getDefaultHostnameVerifier());
+ }
+
+ public void testDefaultHostnameVerifierUsedForNewConnectionsByDefault() throws IOException {
+ HostnameVerifier originalHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+ HttpsURLConnection connection =
+ (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(originalHostnameVerifier, connection.getHostnameVerifier());
+ } finally {
+ connection.disconnect();
+ }
+
+ HostnameVerifier anotherVerifier = new FakeHostnameVerifier();
+ try {
+ HttpsURLConnection.setDefaultHostnameVerifier(anotherVerifier);
+ connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(anotherVerifier, connection.getHostnameVerifier());
+ } finally {
+ connection.disconnect();
+ }
+
+ HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
+ connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(originalHostnameVerifier, connection.getHostnameVerifier());
+ } finally {
+ connection.disconnect();
+ }
+ } finally {
+ HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
+ }
+ }
+
+ public void testDefaultSSLSocketFactoryNotNull() {
+ assertNotNull(HttpsURLConnection.getDefaultSSLSocketFactory());
+ }
+
+ public void testDefaultSSLSocketFactoryUsedForNewConnectionsByDefault() throws IOException {
+ SSLSocketFactory originalFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+ HttpsURLConnection connection =
+ (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(originalFactory, connection.getSSLSocketFactory());
+ } finally {
+ connection.disconnect();
+ }
+
+ SSLSocketFactory anotherFactory = new SSLSocketFactoryTest.FakeSSLSocketFactory();
+ try {
+ HttpsURLConnection.setDefaultSSLSocketFactory(anotherFactory);
+ connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(anotherFactory, connection.getSSLSocketFactory());
+ } finally {
+ connection.disconnect();
+ }
+
+ HttpsURLConnection.setDefaultSSLSocketFactory(originalFactory);
+ connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(originalFactory, connection.getSSLSocketFactory());
+ } finally {
+ connection.disconnect();
+ }
+ } finally {
+ HttpsURLConnection.setDefaultSSLSocketFactory(originalFactory);
+ }
+ }
+
+ private static class FakeHostnameVerifier implements HostnameVerifier {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
index dccadbd..533849c 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
@@ -26,7 +26,6 @@ import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import libcore.io.IoUtils;
@@ -82,14 +81,14 @@ public class SSLContextTest extends TestCase {
}
public void test_SSLContext_defaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLContext(SSLContext.getDefault());
+ SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(SSLContext.getDefault());
for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
SSLContext sslContext = SSLContext.getInstance(protocol);
if (!protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
sslContext.init(null, null, null);
}
- SSLDefaultConfigurationAsserts.assertSSLContext(sslContext);
+ SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(sslContext);
}
}
@@ -149,6 +148,27 @@ public class SSLContextTest extends TestCase {
sslContext);
}
+ public void test_SSLContext_init_correctProtocolVersionsEnabled() throws Exception {
+ for (String tlsVersion : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+ // Don't test the "Default" instance.
+ if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(tlsVersion)) {
+ continue;
+ }
+
+ SSLContext context = SSLContext.getInstance(tlsVersion);
+ context.init(null, null, null);
+
+ StandardNames.assertSSLContextEnabledProtocols(tlsVersion, ((SSLSocket) (context.getSocketFactory()
+ .createSocket())).getEnabledProtocols());
+ StandardNames.assertSSLContextEnabledProtocols(tlsVersion, ((SSLServerSocket) (context
+ .getServerSocketFactory().createServerSocket())).getEnabledProtocols());
+ StandardNames.assertSSLContextEnabledProtocols(tlsVersion, context.getDefaultSSLParameters()
+ .getProtocols());
+ StandardNames.assertSSLContextEnabledProtocols(tlsVersion, context.createSSLEngine()
+ .getEnabledProtocols());
+ }
+ }
+
private static void assertEnabledCipherSuites(
List<String> expectedCipherSuites, SSLContext sslContext) throws Exception {
assertContentsInOrder(
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index df4585f..5e3a3d5 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -65,7 +65,7 @@ public class SSLEngineTest extends TestCase {
}
public void test_SSLEngine_defaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLEngine(
+ SSLConfigurationAsserts.assertSSLEngineDefaultConfiguration(
TestSSLContext.create().clientContext.createSSLEngine());
}
@@ -203,24 +203,31 @@ public class SSLEngineTest extends TestCase {
: new String[] { cipherSuite });
// Check that handshake succeeds.
- TestSSLEnginePair pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
- @Override
- void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
- client.setEnabledCipherSuites(cipherSuiteArray);
- server.setEnabledCipherSuites(cipherSuiteArray);
+ TestSSLEnginePair pair = null;
+ try {
+ pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ client.setEnabledCipherSuites(cipherSuiteArray);
+ server.setEnabledCipherSuites(cipherSuiteArray);
+ }
+ });
+ assertConnected(pair);
+
+ boolean needsRecordSplit =
+ ("TLS".equalsIgnoreCase(c.clientContext.getProtocol())
+ || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol()))
+ && cipherSuite.contains("_CBC_");
+
+ assertSendsCorrectly("This is the client. Hello!".getBytes(),
+ pair.client, pair.server, needsRecordSplit);
+ assertSendsCorrectly("This is the server. Hi!".getBytes(),
+ pair.server, pair.client, needsRecordSplit);
+ } finally {
+ if (pair != null) {
+ pair.close();
}
- });
- assertConnected(pair);
-
- boolean needsRecordSplit =
- ("TLS".equalsIgnoreCase(c.clientContext.getProtocol())
- || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol()))
- && cipherSuite.contains("_CBC_");
-
- assertSendsCorrectly("This is the client. Hello!".getBytes(),
- pair.client, pair.server, needsRecordSplit);
- assertSendsCorrectly("This is the server. Hi!".getBytes(),
- pair.server, pair.client, needsRecordSplit);
+ }
// Check that handshake fails when the server does not possess the private key
// corresponding to the server's certificate. This is achieved by using SSLContext
@@ -234,17 +241,23 @@ public class SSLEngineTest extends TestCase {
serverAuthenticatedUsingPublicKey = false;
}
if (serverAuthenticatedUsingPublicKey) {
+ TestSSLEnginePair p = null;
try {
- TestSSLEnginePair p = TestSSLEnginePair.create(
+ p = TestSSLEnginePair.create(
cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() {
- @Override
+ @Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
- client.setEnabledCipherSuites(cipherSuiteArray);
- server.setEnabledCipherSuites(cipherSuiteArray);
- }
- });
+ client.setEnabledCipherSuites(cipherSuiteArray);
+ server.setEnabledCipherSuites(cipherSuiteArray);
+ }
+ });
assertNotConnected(p);
- } catch (IOException expected) {}
+ } catch (IOException expected) {
+ } finally {
+ if (p != null) {
+ p.close();
+ }
+ }
}
} catch (Exception e) {
String message = ("Problem trying to connect cipher suite " + cipherSuite);
@@ -432,21 +445,28 @@ public class SSLEngineTest extends TestCase {
fail();
} catch (IllegalStateException expected) {
}
+ c.close();
- assertConnected(TestSSLEnginePair.create(null));
+ TestSSLEnginePair p = TestSSLEnginePair.create(null);
+ assertConnected(p);
+ p.close();
- c.close();
}
public void test_SSLEngine_beginHandshake_noKeyStore() throws Exception {
TestSSLContext c = TestSSLContext.create(null, null, null, null, null, null, null, null,
SSLContext.getDefault(), SSLContext.getDefault());
+ SSLEngine[] p = null;
try {
// TODO Fix KnownFailure AlertException "NO SERVER CERTIFICATE FOUND"
// ServerHandshakeImpl.selectSuite should not select a suite without a required cert
- TestSSLEnginePair.connect(c, null);
+ p = TestSSLEnginePair.connect(c, null);
fail();
} catch (SSLHandshakeException expected) {
+ } finally {
+ if (p != null) {
+ TestSSLEnginePair.close(p);
+ }
}
c.close();
}
@@ -456,6 +476,7 @@ public class SSLEngineTest extends TestCase {
SSLEngine[] engines = TestSSLEnginePair.connect(c, null);
assertConnected(engines[0], engines[1]);
c.close();
+ TestSSLEnginePair.close(engines);
}
public void test_SSLEngine_getUseClientMode() throws Exception {
@@ -467,33 +488,47 @@ public class SSLEngineTest extends TestCase {
public void test_SSLEngine_setUseClientMode() throws Exception {
boolean[] finished;
+ TestSSLEnginePair p = null;
// client is client, server is server
finished = new boolean[2];
- assertConnected(test_SSLEngine_setUseClientMode(true, false, finished));
+ p = test_SSLEngine_setUseClientMode(true, false, finished);
+ assertConnected(p);
assertTrue(finished[0]);
assertTrue(finished[1]);
+ p.close();
// client is server, server is client
finished = new boolean[2];
- assertConnected(test_SSLEngine_setUseClientMode(false, true, finished));
+ p = test_SSLEngine_setUseClientMode(false, true, finished);
+ assertConnected(p);
assertTrue(finished[0]);
assertTrue(finished[1]);
+ p.close();
// both are client
/*
* Our implementation throws an SSLHandshakeException, but RI just
* stalls forever
*/
+ p = null;
try {
- assertNotConnected(test_SSLEngine_setUseClientMode(true, true, null));
+ p = test_SSLEngine_setUseClientMode(true, true, null);
+ assertNotConnected(p);
assertTrue(StandardNames.IS_RI);
} catch (SSLHandshakeException maybeExpected) {
assertFalse(StandardNames.IS_RI);
+ } finally {
+ if (p != null) {
+ p.close();
+ }
+
}
+ p = test_SSLEngine_setUseClientMode(false, false, null);
// both are server
- assertNotConnected(test_SSLEngine_setUseClientMode(false, false, null));
+ assertNotConnected(p);
+ p.close();
}
public void test_SSLEngine_setUseClientMode_afterHandshake() throws Exception {
@@ -510,6 +545,7 @@ public class SSLEngineTest extends TestCase {
fail();
} catch (IllegalArgumentException expected) {
}
+ pair.close();
}
private TestSSLEnginePair test_SSLEngine_setUseClientMode(final boolean clientClientMode,
@@ -572,6 +608,7 @@ public class SSLEngineTest extends TestCase {
p.client.getSession().getLocalCertificates());
clientAuthContext.close();
c.close();
+ p.close();
}
/**
@@ -591,6 +628,7 @@ public class SSLEngineTest extends TestCase {
});
assertConnected(p);
clientAuthContext.close();
+ p.close();
}
/**
@@ -604,8 +642,9 @@ public class SSLEngineTest extends TestCase {
TestSSLContext clientAuthContext
= TestSSLContext.create(TestKeyStore.getClient(),
TestKeyStore.getServer());
+ TestSSLEnginePair p = null;
try {
- TestSSLEnginePair.create(clientAuthContext,
+ p = TestSSLEnginePair.create(clientAuthContext,
new TestSSLEnginePair.Hooks() {
@Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
@@ -616,6 +655,9 @@ public class SSLEngineTest extends TestCase {
} catch (SSLHandshakeException expected) {
} finally {
clientAuthContext.close();
+ if (p != null) {
+ p.close();
+ }
}
}
@@ -624,11 +666,13 @@ public class SSLEngineTest extends TestCase {
SSLEngine e = c.clientContext.createSSLEngine();
assertTrue(e.getEnableSessionCreation());
c.close();
+ TestSSLEnginePair.close(new SSLEngine[] { e });
}
public void test_SSLEngine_setEnableSessionCreation_server() throws Exception {
+ TestSSLEnginePair p = null;
try {
- TestSSLEnginePair p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+ p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
@Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
server.setEnableSessionCreation(false);
@@ -639,12 +683,17 @@ public class SSLEngineTest extends TestCase {
assertNotConnected(p);
} catch (SSLException maybeExpected) {
assertFalse(StandardNames.IS_RI);
+ } finally {
+ if (p != null) {
+ p.close();
+ }
}
}
public void test_SSLEngine_setEnableSessionCreation_client() throws Exception {
+ TestSSLEnginePair p = null;
try {
- TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+ p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
@Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
client.setEnableSessionCreation(false);
@@ -652,6 +701,10 @@ public class SSLEngineTest extends TestCase {
});
fail();
} catch (SSLException expected) {
+ } finally {
+ if (p != null) {
+ p.close();
+ }
}
}
@@ -735,5 +788,6 @@ public class SSLEngineTest extends TestCase {
assertNotNull(test.server);
assertNotNull(test.client);
assertConnected(test);
+ test.close();
}
}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
index ea9c3f0..cda1fb8 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
@@ -22,7 +22,7 @@ import junit.framework.TestCase;
public class SSLServerSocketFactoryTest extends TestCase {
public void testDefaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLServerSocketFactory(
+ SSLConfigurationAsserts.assertSSLServerSocketFactoryDefaultConfiguration(
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault());
}
}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
index 59c44c1..d2c0f48 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
@@ -24,7 +24,7 @@ import java.util.Arrays;
public class SSLServerSocketTest extends TestCase {
public void testDefaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLServerSocket(
+ SSLConfigurationAsserts.assertSSLServerSocketDefaultConfiguration(
(SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket());
}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java
index a434d94..bc2b626 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java
@@ -99,7 +99,7 @@ public class SSLSessionTest extends TestCase {
assertTrue("s.server.getLastAccessedTime()=" + s.server.getLastAccessedTime() + " " +
"s.client.getLastAccessedTime()=" + s.client.getLastAccessedTime(),
Math.abs(s.server.getLastAccessedTime()
- - s.client.getLastAccessedTime()) < 1 * 1000);
+ - s.client.getLastAccessedTime()) <= 1 * 1000);
assertTrue(s.server.getLastAccessedTime() >=
s.server.getCreationTime());
assertTrue(s.client.getLastAccessedTime() >=
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
index acf69c0..83b690b 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
@@ -210,7 +210,7 @@ public class SSLSocketFactoryTest extends TestCase {
}
public void test_SSLSocketFactory_defaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLSocketFactory(
+ SSLConfigurationAsserts.assertSSLSocketFactoryDefaultConfiguration(
(SSLSocketFactory) SSLSocketFactory.getDefault());
}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
index 4af7f5a..bf2d0f8 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -16,6 +16,8 @@
package libcore.javax.net.ssl;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
@@ -26,15 +28,18 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
-import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -66,11 +71,23 @@ import libcore.io.IoUtils;
import libcore.io.Streams;
import libcore.java.security.StandardNames;
import libcore.java.security.TestKeyStore;
+import libcore.tlswire.handshake.CipherSuite;
+import libcore.tlswire.handshake.ClientHello;
+import libcore.tlswire.handshake.CompressionMethod;
+import libcore.tlswire.handshake.HandshakeMessage;
+import libcore.tlswire.handshake.HelloExtension;
+import libcore.tlswire.handshake.ServerNameHelloExtension;
+import libcore.tlswire.record.TlsProtocols;
+import libcore.tlswire.record.TlsRecord;
+import libcore.tlswire.util.TlsProtocolVersion;
+import tests.util.ForEachRunner;
+import tests.util.DelegatingSSLSocketFactory;
+import tests.util.Pair;
public class SSLSocketTest extends TestCase {
public void test_SSLSocket_defaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLSocket(
+ SSLConfigurationAsserts.assertSSLSocketDefaultConfiguration(
(SSLSocket) SSLSocketFactory.getDefault().createSocket());
}
@@ -403,6 +420,37 @@ public class SSLSocketTest extends TestCase {
c.close();
}
+ public void test_SSLSocket_NoEnabledCipherSuites_Failure() throws Exception {
+ TestSSLContext c = TestSSLContext.create(null, null, null, null, null, null, null, null,
+ SSLContext.getDefault(), SSLContext.getDefault());
+ SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host,
+ c.port);
+ client.setEnabledCipherSuites(new String[0]);
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Void> future = executor.submit(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ try {
+ server.startHandshake();
+ fail();
+ } catch (SSLHandshakeException expected) {
+ }
+ return null;
+ }
+ });
+ executor.shutdown();
+ try {
+ client.startHandshake();
+ fail();
+ } catch (SSLHandshakeException expected) {
+ }
+ future.get();
+ server.close();
+ client.close();
+ c.close();
+ }
+
public void test_SSLSocket_startHandshake_noKeyStore() throws Exception {
TestSSLContext c = TestSSLContext.create(null, null, null, null, null, null, null, null,
SSLContext.getDefault(), SSLContext.getDefault());
@@ -1458,11 +1506,147 @@ public class SSLSocketTest extends TestCase {
test.close();
}
- public void test_SSLSocket_ClientHello_size() throws Exception {
+ public void test_SSLSocket_ClientHello_record_size() throws Exception {
// This test checks the size of ClientHello of the default SSLSocket. TLS/SSL handshakes
// with older/unpatched F5/BIG-IP appliances are known to stall and time out when
// the fragment containing ClientHello is between 256 and 511 (inclusive) bytes long.
- //
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, null, null);
+ SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+ sslSocketFactory = new DelegatingSSLSocketFactory(sslSocketFactory) {
+ @Override
+ protected void configureSocket(SSLSocket socket) {
+ // Enable SNI extension on the socket (this is typically enabled by default)
+ // to increase the size of ClientHello.
+ try {
+ Method setHostname =
+ socket.getClass().getMethod("setHostname", String.class);
+ setHostname.invoke(socket, "sslsockettest.androidcts.google.com");
+ } catch (NoSuchMethodException ignored) {
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to enable SNI", e);
+ }
+
+ // Enable Session Tickets extension on the socket (this is typically enabled
+ // by default) to increase the size of ClientHello.
+ try {
+ Method setUseSessionTickets =
+ socket.getClass().getMethod(
+ "setUseSessionTickets", boolean.class);
+ setUseSessionTickets.invoke(socket, true);
+ } catch (NoSuchMethodException ignored) {
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to enable Session Tickets", e);
+ }
+ }
+ };
+
+ TlsRecord firstReceivedTlsRecord = captureTlsHandshakeFirstTlsRecord(sslSocketFactory);
+ assertEquals("TLS record type", TlsProtocols.HANDSHAKE, firstReceivedTlsRecord.type);
+ HandshakeMessage handshakeMessage = HandshakeMessage.read(
+ new DataInputStream(new ByteArrayInputStream(firstReceivedTlsRecord.fragment)));
+ assertEquals("HandshakeMessage type",
+ HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+ int fragmentLength = firstReceivedTlsRecord.fragment.length;
+ if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
+ fail("Fragment containing ClientHello is of dangerous length: "
+ + fragmentLength + " bytes");
+ }
+ }
+
+ public void test_SSLSocket_ClientHello_cipherSuites() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ String[] cipherSuites = new String[clientHello.cipherSuites.size()];
+ for (int i = 0; i < clientHello.cipherSuites.size(); i++) {
+ CipherSuite cipherSuite = clientHello.cipherSuites.get(i);
+ cipherSuites[i] = cipherSuite.getAndroidName();
+ }
+ StandardNames.assertDefaultCipherSuites(cipherSuites);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ assertEquals(TlsProtocolVersion.TLSv1_2, clientHello.clientVersion);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_compressionMethods() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ assertEquals(Arrays.asList(CompressionMethod.NULL), clientHello.compressionMethods);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_SNI() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ ServerNameHelloExtension sniExtension = (ServerNameHelloExtension)
+ clientHello.findExtensionByType(HelloExtension.TYPE_SERVER_NAME);
+ assertNotNull(sniExtension);
+ assertEquals(Arrays.asList("localhost.localdomain"), sniExtension.hostnames);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ private List<Pair<String, SSLSocketFactory>> getSSLSocketFactoriesToTest()
+ throws NoSuchAlgorithmException, KeyManagementException {
+ List<Pair<String, SSLSocketFactory>> result =
+ new ArrayList<Pair<String, SSLSocketFactory>>();
+ result.add(Pair.of("default", (SSLSocketFactory) SSLSocketFactory.getDefault()));
+ for (String sslContextProtocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+ SSLContext sslContext = SSLContext.getInstance(sslContextProtocol);
+ if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(sslContextProtocol)) {
+ continue;
+ }
+ sslContext.init(null, null, null);
+ result.add(Pair.of(
+ "SSLContext(\"" + sslContext.getProtocol() + "\")",
+ sslContext.getSocketFactory()));
+ }
+ return result;
+ }
+
+ private ClientHello captureTlsHandshakeClientHello(SSLSocketFactory sslSocketFactory)
+ throws Exception {
+ TlsRecord record = captureTlsHandshakeFirstTlsRecord(sslSocketFactory);
+ assertEquals("TLS record type", TlsProtocols.HANDSHAKE, record.type);
+ ByteArrayInputStream fragmentIn = new ByteArrayInputStream(record.fragment);
+ HandshakeMessage handshakeMessage = HandshakeMessage.read(new DataInputStream(fragmentIn));
+ assertEquals("HandshakeMessage type",
+ HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+ // Assert that the fragment does not contain any more messages
+ assertEquals(0, fragmentIn.available());
+
+ return (ClientHello) handshakeMessage;
+ }
+
+ private TlsRecord captureTlsHandshakeFirstTlsRecord(SSLSocketFactory sslSocketFactory)
+ throws Exception {
+ byte[] firstReceivedChunk = captureTlsHandshakeFirstTransmittedChunkBytes(sslSocketFactory);
+ ByteArrayInputStream firstReceivedChunkIn = new ByteArrayInputStream(firstReceivedChunk);
+ TlsRecord record = TlsRecord.read(new DataInputStream(firstReceivedChunkIn));
+ // Assert that the chunk does not contain any more data
+ assertEquals(0, firstReceivedChunkIn.available());
+
+ return record;
+ }
+
+ private byte[] captureTlsHandshakeFirstTransmittedChunkBytes(
+ final SSLSocketFactory sslSocketFactory) throws Exception {
// Since there's no straightforward way to obtain a ClientHello from SSLSocket, this test
// does the following:
// 1. Creates a listening server socket (a plain one rather than a TLS/SSL one).
@@ -1506,61 +1690,31 @@ public class SSLSocketTest extends TestCase {
executorService.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, null, null);
- SSLSocket client = (SSLSocket) sslContext.getSocketFactory().createSocket();
+ Socket client = new Socket();
sockets[0] = client;
try {
- // Enable SNI extension on the socket (this is typically enabled by default)
- // to increase the size of ClientHello.
- try {
- Method setHostname =
- client.getClass().getMethod("setHostname", String.class);
- setHostname.invoke(client, "sslsockettest.androidcts.google.com");
- } catch (NoSuchMethodException ignored) {}
-
- // Enable Session Tickets extension on the socket (this is typically enabled
- // by default) to increase the size of ClientHello.
- try {
- Method setUseSessionTickets =
- client.getClass().getMethod(
- "setUseSessionTickets", boolean.class);
- setUseSessionTickets.invoke(client, true);
- } catch (NoSuchMethodException ignored) {}
-
client.connect(finalListeningSocket.getLocalSocketAddress());
// Initiate the TLS/SSL handshake which is expected to fail as soon as the
// server socket receives a ClientHello.
try {
- client.startHandshake();
+ SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
+ client,
+ "localhost.localdomain",
+ finalListeningSocket.getLocalPort(),
+ true);
+ sslSocket.startHandshake();
fail();
return null;
} catch (IOException expected) {}
return null;
} finally {
IoUtils.closeQuietly(client);
-
- // Cancel the reading task. If this task succeeded, then the reading task
- // is done and this will have no effect. If this task failed prematurely,
- // then the reading task might get unblocked (we're interrupting the thread
- // it's running on), will fail early, and we'll thus save some time in this
- // test.
- readFirstReceivedChunkFuture.cancel(true);
}
}
});
// Wait for the ClientHello to arrive
- byte[] clientHello = readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
-
- // Check for ClientHello length that may cause handshake to fail/time out with older
- // F5/BIG-IP appliances.
- assertEquals("TLS record type: handshake", 22, clientHello[0]);
- int fragmentLength = ((clientHello[3] & 0xff) << 8) | (clientHello[4] & 0xff);
- if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
- fail("Fragment containing ClientHello is of dangerous length: "
- + fragmentLength + " bytes");
- }
+ return readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
} finally {
executorService.shutdownNow();
IoUtils.closeQuietly(listeningSocket);
@@ -1668,6 +1822,11 @@ public class SSLSocketTest extends TestCase {
context.close();
}
+ private static void assertInappropriateFallbackIsCause(Throwable cause) {
+ assertTrue(cause.getMessage(), cause.getMessage().contains("inappropriate fallback")
+ || cause.getMessage().contains("INAPPROPRIATE_FALLBACK"));
+ }
+
public void test_SSLSocket_sendsTlsFallbackScsv_InappropriateFallback_Failure() throws Exception {
TestSSLContext context = TestSSLContext.create();
@@ -1693,8 +1852,7 @@ public class SSLSocketTest extends TestCase {
} catch (SSLHandshakeException expected) {
Throwable cause = expected.getCause();
assertEquals(SSLProtocolException.class, cause.getClass());
- assertTrue(cause.getMessage(),
- cause.getMessage().contains("inappropriate fallback"));
+ assertInappropriateFallbackIsCause(cause);
}
return null;
}
@@ -1709,8 +1867,7 @@ public class SSLSocketTest extends TestCase {
} catch (SSLHandshakeException expected) {
Throwable cause = expected.getCause();
assertEquals(SSLProtocolException.class, cause.getClass());
- assertTrue(cause.getMessage(),
- cause.getMessage().contains("inappropriate fallback"));
+ assertInappropriateFallbackIsCause(cause);
}
return null;
}
diff --git a/luni/src/test/java/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/net/MimeUtilsTest.java
index 9bfb375..ff22632 100644
--- a/luni/src/test/java/libcore/net/MimeUtilsTest.java
+++ b/luni/src/test/java/libcore/net/MimeUtilsTest.java
@@ -27,6 +27,12 @@ public class MimeUtilsTest extends TestCase {
assertEquals("flac", MimeUtils.guessExtensionFromMimeType("application/x-flac"));
}
+ // https://code.google.com/p/android/issues/detail?id=78909
+ public void test_78909() {
+ assertEquals("mka", MimeUtils.guessExtensionFromMimeType("audio/x-matroska"));
+ assertEquals("mkv", MimeUtils.guessExtensionFromMimeType("video/x-matroska"));
+ }
+
public void test_16978217() {
assertEquals("image/x-ms-bmp", MimeUtils.guessMimeTypeFromExtension("bmp"));
assertEquals("image/x-icon", MimeUtils.guessMimeTypeFromExtension("ico"));
diff --git a/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java b/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java
new file mode 100644
index 0000000..d04e2ba
--- /dev/null
+++ b/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2015 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 libcore.net;
+
+import junit.framework.TestCase;
+import libcore.io.IoUtils;
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.ErrorManager;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.SocketHandler;
+
+public class NetworkSecurityPolicyTest extends TestCase {
+
+ private boolean mCleartextTrafficPermittedOriginalState;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCleartextTrafficPermittedOriginalState =
+ NetworkSecurityPolicy.isCleartextTrafficPermitted();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(
+ mCleartextTrafficPermittedOriginalState);
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ public void testCleartextTrafficPolicySetterAndGetter() {
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ assertEquals(false, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ assertEquals(true, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ assertEquals(false, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ assertEquals(true, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+ }
+
+ public void testCleartextTrafficPolicyWithHttpURLConnection() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
+ try {
+ url.openConnection().getContent();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted and
+ // that URLConnection.openConnection or getContent fail with an IOException.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
+ try {
+ url.openConnection().getContent();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ public void testCleartextTrafficPolicyWithFtpURLConnection() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
+ try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+ URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
+ try {
+ url.openConnection().getContent();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted and
+ // that URLConnection.openConnection or getContent fail with an IOException.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+ URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
+ try {
+ url.openConnection().getContent();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ public void testCleartextTrafficPolicyWithJarHttpURLConnection() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
+ try {
+ ((JarURLConnection) url.openConnection()).getManifest();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted and
+ // that JarURLConnection.openConnection or getManifest fail with an IOException.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
+ try {
+ ((JarURLConnection) url.openConnection()).getManifest();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ public void testCleartextTrafficPolicyWithJarFtpURLConnection() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
+ try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+ URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
+ try {
+ ((JarURLConnection) url.openConnection()).getManifest();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted and
+ // that JarURLConnection.openConnection or getManifest fail with an IOException.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+ URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
+ try {
+ ((JarURLConnection) url.openConnection()).getManifest();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ public void testCleartextTrafficPolicyWithLoggingSocketHandler() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ SocketHandler logger = new SocketHandler("localhost", server.getPort());
+ MockErrorManager mockErrorManager = new MockErrorManager();
+ logger.setErrorManager(mockErrorManager);
+ logger.setLevel(Level.ALL);
+ LogRecord record = new LogRecord(Level.INFO, "A log record");
+ assertTrue(logger.isLoggable(record));
+ logger.publish(record);
+ assertNull(mockErrorManager.getMostRecentException());
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ try {
+ new SocketHandler("localhost", server.getPort());
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ /**
+ * Server socket which listens on a local port and captures the first chunk of data transmitted
+ * by the client.
+ */
+ private static class CapturingServerSocket implements Closeable {
+ private final ServerSocket mSocket;
+ private final int mPort;
+ private final Thread mListeningThread;
+ private final FutureTask<byte[]> mFirstChunkReceivedFuture;
+
+ /**
+ * Constructs a new socket listening on a local port.
+ */
+ public CapturingServerSocket() throws IOException {
+ this(null);
+ }
+
+ /**
+ * Constructs a new socket listening on a local port, which sends the provided reply as
+ * soon as a client connects to it.
+ */
+ public CapturingServerSocket(final byte[] replyOnConnect) throws IOException {
+ mSocket = new ServerSocket(0);
+ mPort = mSocket.getLocalPort();
+ mFirstChunkReceivedFuture = new FutureTask<byte[]>(new Callable<byte[]>() {
+ @Override
+ public byte[] call() throws Exception {
+ try (Socket client = mSocket.accept()) {
+ // Reply (if requested)
+ if (replyOnConnect != null) {
+ client.getOutputStream().write(replyOnConnect);
+ client.getOutputStream().flush();
+ }
+
+ // Read request
+ byte[] buf = new byte[64 * 1024];
+ int chunkSize = client.getInputStream().read(buf);
+ if (chunkSize == -1) {
+ // Connection closed without any data received
+ return new byte[0];
+ }
+ // Received some data
+ return Arrays.copyOf(buf, chunkSize);
+ } finally {
+ IoUtils.closeQuietly(mSocket);
+ }
+ }
+ });
+ mListeningThread = new Thread(mFirstChunkReceivedFuture);
+ mListeningThread.start();
+ }
+
+ public int getPort() {
+ return mPort;
+ }
+
+ public Future<byte[]> getFirstReceivedChunkFuture() {
+ return mFirstChunkReceivedFuture;
+ }
+
+ @Override
+ public void close() {
+ IoUtils.closeQuietly(mSocket);
+ mListeningThread.interrupt();
+ }
+
+ private void assertDataTransmittedByClient()
+ throws Exception {
+ byte[] firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS);
+ if ((firstChunkFromClient == null) || (firstChunkFromClient.length == 0)) {
+ fail("Client did not transmit any data to server");
+ }
+ }
+
+ private void assertNoDataTransmittedByClient()
+ throws Exception {
+ byte[] firstChunkFromClient;
+ try {
+ firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS);
+ } catch (TimeoutException expected) {
+ return;
+ }
+ if ((firstChunkFromClient != null) && (firstChunkFromClient.length > 0)) {
+ fail("Client transmitted " + firstChunkFromClient.length+ " bytes: "
+ + new String(firstChunkFromClient, "US-ASCII"));
+ }
+ }
+ }
+
+ private static class MockErrorManager extends ErrorManager {
+ private Exception mMostRecentException;
+
+ public Exception getMostRecentException() {
+ synchronized (this) {
+ return mMostRecentException;
+ }
+ }
+
+ @Override
+ public void error(String message, Exception exception, int errorCode) {
+ synchronized (this) {
+ mMostRecentException = exception;
+ }
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java b/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java
new file mode 100644
index 0000000..16745ee
--- /dev/null
+++ b/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 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 libcore.net.http;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+import junit.framework.TestCase;
+import static libcore.net.http.ResponseUtils.responseCharset;
+
+public class ResponseUtilsTest extends TestCase {
+ public void test_responseCharset_missing() {
+ assertEquals(StandardCharsets.UTF_8, responseCharset(null));
+ assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain"));
+ assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain;foo=bar;baz=bal"));
+ assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain;charset="));
+ }
+
+ public void test_responseCharset_valid() {
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain;charset=ISO-8859-1"));
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain;CHARSET=ISO-8859-1"));
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain; charset = ISO-8859-1"));
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain; foo=bar;baz=bag;charset=ISO-8859-1"));
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain;charset=ISO-8859-1;;==,=="));
+ }
+
+ public void test_responseCharset_invalid() {
+ try {
+ responseCharset("text/plain;charset=unsupportedCharset");
+ fail();
+ } catch (UnsupportedCharsetException expected) {
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/util/HexEncodingTest.java b/luni/src/test/java/libcore/util/HexEncodingTest.java
new file mode 100644
index 0000000..f5cfb3e
--- /dev/null
+++ b/luni/src/test/java/libcore/util/HexEncodingTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 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 libcore.util;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import junit.framework.TestCase;
+import static libcore.util.HexEncoding.decode;
+import static libcore.util.HexEncoding.encode;
+
+public class HexEncodingTest extends TestCase {
+ public void testEncode() {
+ final byte[] avocados = "avocados".getBytes(StandardCharsets.UTF_8);
+
+ assertArraysEqual("61766F6361646F73".toCharArray(), encode(avocados));
+ assertArraysEqual(avocados, decode(encode(avocados), false));
+ // Make sure we can handle lower case hex encodings as well.
+ assertArraysEqual(avocados, decode("61766f6361646f73".toCharArray(), false));
+ }
+
+ public void testDecode_allow4Bit() {
+ assertArraysEqual(new byte[] { 6 }, decode("6".toCharArray(), true));
+ assertArraysEqual(new byte[] { 6, 'v' }, decode("676".toCharArray(), true));
+ }
+
+ public void testDecode_disallow4Bit() {
+ try {
+ decode("676".toCharArray(), false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testDecode_invalid() {
+ try {
+ decode("DEADBARD".toCharArray(), false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // This demonstrates a difference in behaviour from apache commons : apache
+ // commons uses Character.isDigit and would successfully decode a string with
+ // arabic and devanagari characters.
+ try {
+ decode("६१٧٥٥F6361646F73".toCharArray(), false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ decode("#%6361646F73".toCharArray(), false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private static void assertArraysEqual(char[] lhs, char[] rhs) {
+ assertEquals(new String(lhs), new String(rhs));
+ }
+
+ private static void assertArraysEqual(byte[] lhs, byte[] rhs) {
+ assertEquals(Arrays.toString(lhs), Arrays.toString(rhs));
+ }
+}
diff --git a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
index 9875647..a90bb8e 100644
--- a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
+++ b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
@@ -98,6 +98,13 @@ public class ZoneInfoDBTest extends junit.framework.TestCase {
public void testMakeTimeZone_notFound() throws Exception {
ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT);
assertNull(data.makeTimeZone("THIS_TZ_DOES_NOT_EXIST"));
+ assertFalse(data.hasTimeZone("THIS_TZ_DOES_NOT_EXIST"));
+ }
+
+ public void testMakeTimeZone_found() throws Exception {
+ ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT);
+ assertNotNull(data.makeTimeZone("Europe/London"));
+ assertTrue(data.hasTimeZone("Europe/London"));
}
private static String makeCorruptFile() throws Exception {
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
index 87b2913..17fb127 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
@@ -28,7 +28,6 @@ import java.security.Provider;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
-import java.util.Vector;
import javax.crypto.ExemptionMechanism;
import javax.crypto.ExemptionMechanismException;
@@ -37,7 +36,6 @@ import javax.crypto.KeyGenerator;
import javax.crypto.ShortBufferException;
import org.apache.harmony.crypto.tests.support.MyExemptionMechanismSpi;
-import org.apache.harmony.crypto.tests.support.MyExemptionMechanismSpi.tmpKey;
import org.apache.harmony.security.tests.support.SpiEngUtils;
import junit.framework.TestCase;
@@ -170,45 +168,6 @@ public class ExemptionMechanismTest extends TestCase {
em.genExemptionBlob(new byte[10], -5);
}
- static boolean flag = false;
-
- class Mock_ExemptionMechanism extends ExemptionMechanism {
- protected Mock_ExemptionMechanism(ExemptionMechanismSpi exmechSpi, Provider provider, String mechanism) {
- super(exmechSpi, provider, mechanism);
- }
-
- @Override
- protected void finalize() {
- flag = true;
- super.finalize();
- }
- }
-
- // Side Effect: Causes OutOfMemoryError to test finalization
- public void test_finalize () {
- Mock_ExemptionMechanism mem = new Mock_ExemptionMechanism(null, null, "Name");
- assertNotNull(mem);
- mem = null;
- assertFalse(flag);
- Vector v = new Vector();
- int capacity;
- try {
- while(true) {
- v.add(this);
- }
- } catch (OutOfMemoryError e) {
- capacity = v.size();
- v = null;
- }
-
- v = new Vector();
- for (int i = 0; i < capacity/2; i++) {
- v.add(this);
- }
- v = null;
- assertTrue(flag);
- }
-
class Mock_ExemptionMechanismSpi extends MyExemptionMechanismSpi {
@Override
protected byte[] engineGenExemptionBlob()
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
index ddd0695..48d945b 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
@@ -76,8 +76,13 @@ public class MacTest extends TestCase {
static {
for (int i = 0; i < validAlgorithmsMac.length; i++) {
- defaultProvider = SpiEngUtils.isSupport(validAlgorithmsMac[i],
- srvMac);
+ try {
+ Mac mac = Mac.getInstance(validAlgorithmsMac[i]);
+ mac.init(new SecretKeySpec(new byte[64], validAlgorithmsMac[i]));
+ defaultProvider = mac.getProvider();
+ } catch (NoSuchAlgorithmException ignored) {
+ } catch (InvalidKeyException ignored) {}
+
DEFSupported = (defaultProvider != null);
if (DEFSupported) {
defaultAlgorithm = validAlgorithmsMac[i];
@@ -100,6 +105,12 @@ public class MacTest extends TestCase {
macList.add(Mac.getInstance(defaultAlgorithm, defaultProvider));
macList.add(Mac.getInstance(defaultAlgorithm, defaultProviderName));
for (Provider p : Security.getProviders("Mac." + defaultAlgorithm)) {
+ // Do not test AndroidKeyStore's Mac. It cannot be initialized without providing an
+ // AndroidKeyStore-backed SecretKey instance. It's OKish not to test here because it's
+ // tested by cts/tests/test/keystore.
+ if (p.getName().startsWith("AndroidKeyStore")) {
+ continue;
+ }
macList.add(Mac.getInstance(defaultAlgorithm, p));
}
return macList.toArray(new Mac[macList.size()]);
@@ -845,6 +856,13 @@ public class MacTest extends TestCase {
byte[] output = null;
byte[] output2 = null;
for (int i = 0; i < providers.length; i++) {
+ // Do not test AndroidKeyStore's Mac. It cannot be initialized without providing an
+ // AndroidKeyStore-backed SecretKey instance. It's OKish not to test here because it's
+ // tested by cts/tests/test/keystore.
+ if (providers[i].getName().startsWith("AndroidKeyStore")) {
+ continue;
+ }
+
System.out.println("provider = " + providers[i].getName());
Mac mac = Mac.getInstance("HmacMD5", providers[i]);
mac.init(key);
@@ -884,6 +902,24 @@ public class MacTest extends TestCase {
public abstract void setup();
}
+ public void testMac_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+ put("Mac.FOO SupportedKeyClasses", "None");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Mac s = Mac.getInstance("FOO", mockProvider);
+ s.init(new MockKey());
+ assertEquals(mockProvider, s.getProvider());
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
public void testMac_getInstance_SuppliedProviderNotRegistered_Success() throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/Signature2Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/Signature2Test.java
index ad084e1..22e6795 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/Signature2Test.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/Signature2Test.java
@@ -478,17 +478,10 @@ public class Signature2Test extends junit.framework.TestCase {
} catch (IllegalArgumentException expected) {
}
- if (StandardNames.IS_RI) {
- try {
- sig.verify(signature, signature.length, 0);
- fail();
- } catch (SignatureException expected) {
- }
- } else {
- // Calling Signature.verify a second time should not throw
- // http://code.google.com/p/android/issues/detail?id=34933
- boolean verified = sig.verify(signature, signature.length, 0);
- assertFalse(verified);
+ try {
+ sig.verify(signature, signature.length, 0);
+ fail();
+ } catch (SignatureException expected) {
}
try {
diff --git a/luni/src/test/java/tests/security/cert/CertificateTest.java b/luni/src/test/java/tests/security/cert/CertificateTest.java
index d13e16b..194bfdb 100644
--- a/luni/src/test/java/tests/security/cert/CertificateTest.java
+++ b/luni/src/test/java/tests/security/cert/CertificateTest.java
@@ -300,17 +300,10 @@ public class MyModifiablePublicKey implements PublicKey {
private Certificate cert;
- private Provider wrongProvider;
-
- private Provider usefulProvider;
-
public void setUp() throws Exception {
super.setUp();
TestUtils.initCertPathSSCertChain();
cert = TestUtils.rootCertificateSS;
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- wrongProvider = cf.getProvider();
- usefulProvider = Signature.getInstance("SHA1WithRSA").getProvider();
}
/**
@@ -326,8 +319,11 @@ public class MyModifiablePublicKey implements PublicKey {
CertificateException, NoSuchAlgorithmException,
NoSuchProviderException, SignatureException {
+ final Signature sig = Signature.getInstance("SHA1WithRSA");
+ sig.initVerify(cert.getPublicKey());
+ final Provider provider = sig.getProvider();
// real test
- cert.verify(cert.getPublicKey(), usefulProvider.getName());
+ cert.verify(cert.getPublicKey(), provider.getName());
// Exception tests
@@ -342,6 +338,9 @@ public class MyModifiablePublicKey implements PublicKey {
// a new provider, test if it works, then remove it and test if the
// exception is thrown.
//
+ // CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ // Provider wrongProvider = cf.getProvider();
+ //
// Security.removeProvider(wrongProvider.getName());
//
// try {
diff --git a/luni/src/test/java/tests/security/interfaces/DSAPrivateKeyTest.java b/luni/src/test/java/tests/security/interfaces/DSAPrivateKeyTest.java
index 6cebda5..5f4abdd 100644
--- a/luni/src/test/java/tests/security/interfaces/DSAPrivateKeyTest.java
+++ b/luni/src/test/java/tests/security/interfaces/DSAPrivateKeyTest.java
@@ -32,9 +32,7 @@ public class DSAPrivateKeyTest extends TestCase {
*/
public void test_getX() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
- keyGen.initialize(new DSAParameterSpec(Util.P, Util.Q, Util.G),
- new SecureRandom(new MySecureRandomSpi(), null) {
- });
+ keyGen.initialize(new DSAParameterSpec(Util.P, Util.Q, Util.G), new SecureRandom());
KeyPair keyPair = keyGen.generateKeyPair();
DSAPrivateKey key = (DSAPrivateKey) keyPair.getPrivate();
assertNotNull("Invalid X value", key.getX());
diff --git a/luni/src/test/java/tests/security/interfaces/DSAPublicKeyTest.java b/luni/src/test/java/tests/security/interfaces/DSAPublicKeyTest.java
index 9fe4910..09e936d 100644
--- a/luni/src/test/java/tests/security/interfaces/DSAPublicKeyTest.java
+++ b/luni/src/test/java/tests/security/interfaces/DSAPublicKeyTest.java
@@ -42,9 +42,7 @@ public class DSAPublicKeyTest extends TestCase {
// Case 1: check with predefined p, q, g, x
keyGen = KeyPairGenerator.getInstance("DSA");
- keyGen.initialize(new DSAParameterSpec(Util.P, Util.Q, Util.G),
- new SecureRandom(new MySecureRandomSpi(), null) {
- });
+ keyGen.initialize(new DSAParameterSpec(Util.P, Util.Q, Util.G), new SecureRandom());
keys = keyGen.generateKeyPair();
priv = (DSAPrivateKey) keys.getPrivate();
publ = (DSAPublicKey) keys.getPublic();
diff --git a/luni/src/test/native/libcore_io_Memory_test.cpp b/luni/src/test/native/libcore_io_Memory_test.cpp
new file mode 100644
index 0000000..2d95155
--- /dev/null
+++ b/luni/src/test/native/libcore_io_Memory_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "luni/src/main/native/libcore_io_Memory.cpp"
+
+#include <stdlib.h>
+
+#include <functional>
+
+#include <gtest/gtest.h>
+
+#define ALIGNMENT 8
+
+template<typename T, size_t NUM_ELEMENTS>
+void swap_align_test(void (*swap_func)(T*, const T*, size_t),
+ std::function<void (T*, T*, uint64_t)> init_func) {
+ uint8_t* dst = nullptr;
+ uint8_t* src = nullptr;
+ ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&dst), ALIGNMENT,
+ sizeof(T) * NUM_ELEMENTS + ALIGNMENT));
+ ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&src), ALIGNMENT,
+ sizeof(T) * NUM_ELEMENTS + ALIGNMENT));
+
+ T src_buf[NUM_ELEMENTS];
+ T dst_buf[NUM_ELEMENTS];
+ for (uint64_t i = 0; i < NUM_ELEMENTS; i++) {
+ init_func(&src_buf[i], &dst_buf[i], i);
+ }
+
+ // Vary a few alignments.
+ for (size_t dst_align = 0; dst_align < ALIGNMENT; dst_align++) {
+ T* dst_aligned = reinterpret_cast<T*>(&dst[dst_align]);
+ for (size_t src_align = 0; src_align < ALIGNMENT; src_align++) {
+ T* src_aligned = reinterpret_cast<T*>(&src[src_align]);
+ memset(dst_aligned, 0, sizeof(T) * NUM_ELEMENTS);
+ memcpy(src_aligned, src_buf, sizeof(T) * NUM_ELEMENTS);
+ swap_func(dst_aligned, src_aligned, NUM_ELEMENTS);
+ ASSERT_EQ(0, memcmp(dst_buf, dst_aligned, sizeof(T) * NUM_ELEMENTS))
+ << "Failed at dst align " << dst_align << " src align " << src_align;
+ }
+ }
+ free(dst);
+ free(src);
+}
+
+TEST(libcore, swapShorts_align_test) {
+ // Use an odd number to guarantee that the last 16-bit swap code
+ // is executed.
+ swap_align_test<jshort, 9> (swapShorts, [] (jshort* src, jshort* dst, uint64_t i) {
+ *src = ((2*i) << 8) | (2*(i+1));
+ *dst = (2*i) | ((2*(i+1)) << 8);
+ });
+}
+
+TEST(libcore, swapInts_align_test) {
+ swap_align_test<jint, 10> (swapInts, [] (jint* src, jint* dst, uint64_t i) {
+ *src = ((4*i) << 24) | ((4*(i+1)) << 16) | ((4*(i+2)) << 8) | (4*(i+3));
+ *dst = (4*i) | ((4*(i+1)) << 8) | ((4*(i+2)) << 16) | ((4*(i+3)) << 24);
+ });
+}
+
+TEST(libcore, swapLongs_align_test) {
+ swap_align_test<jlong, 10> (swapLongs, [] (jlong* src, jlong* dst, uint64_t i) {
+ *src = ((8*i) << 56) | ((8*(i+1)) << 48) | ((8*(i+2)) << 40) | ((8*(i+3)) << 32) |
+ ((8*(i+4)) << 24) | ((8*(i+5)) << 16) | ((8*(i+6)) << 8) | (8*(i+7));
+ *dst = (8*i) | ((8*(i+1)) << 8) | ((8*(i+2)) << 16) | ((8*(i+3)) << 24) |
+ ((8*(i+4)) << 32) | ((8*(i+5)) << 40) | ((8*(i+6)) << 48) | ((8*(i+7)) << 56);
+ });
+}
+
+template<typename T>
+void memory_peek_test(T (*peek_func)(JNIEnv*, jclass, jlong), T value) {
+ T* src = nullptr;
+ ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&src), ALIGNMENT,
+ sizeof(T) + ALIGNMENT));
+ for (size_t i = 0; i < ALIGNMENT; i++) {
+ jlong src_aligned = reinterpret_cast<jlong>(src) + i;
+ memcpy(reinterpret_cast<void*>(src_aligned), &value, sizeof(T));
+ T result = peek_func(nullptr, nullptr, src_aligned);
+ ASSERT_EQ(value, result);
+ }
+ free(src);
+}
+
+TEST(libcore, Memory_peekShortNative_align_check) {
+ memory_peek_test<jshort>(Memory_peekShortNative, 0x0102);
+}
+
+TEST(libcore, Memory_peekIntNative_align_check) {
+ memory_peek_test<jint>(Memory_peekIntNative, 0x01020304);
+}
+
+TEST(libcore, Memory_peekLongNative_align_check) {
+ memory_peek_test<jlong>(Memory_peekLongNative, 0x01020405060708ULL);
+}
+
+template<typename T>
+void memory_poke_test(void (*poke_func)(JNIEnv*, jclass, jlong, T), T value) {
+ T* dst = nullptr;
+ ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&dst), ALIGNMENT,
+ sizeof(T) + ALIGNMENT));
+ for(size_t i = 0; i < ALIGNMENT; i++) {
+ memset(dst, 0, sizeof(T) + ALIGNMENT);
+ jlong dst_aligned = reinterpret_cast<jlong>(dst) + i;
+ poke_func(nullptr, nullptr, dst_aligned, value);
+ ASSERT_EQ(0, memcmp(reinterpret_cast<void*>(dst_aligned), &value, sizeof(T)));
+ }
+ free(dst);
+}
+
+TEST(libcore, Memory_pokeShortNative_align_check) {
+ memory_poke_test<jshort>(Memory_pokeShortNative, 0x0102);
+}
+
+TEST(libcore, Memory_pokeIntNative_align_check) {
+ memory_poke_test<jint>(Memory_pokeIntNative, 0x01020304);
+}
+
+TEST(libcore, Memory_pokeLongNative_align_check) {
+ memory_poke_test<jlong>(Memory_pokeLongNative, 0x0102030405060708ULL);
+}
diff --git a/luni/src/test/native/test_openssl_engine.cpp b/luni/src/test/native/test_openssl_engine.cpp
deleted file mode 100644
index 9a0f3b3..0000000
--- a/luni/src/test/native/test_openssl_engine.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#include "UniquePtr.h"
-
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/objects.h>
-#include <openssl/engine.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-
-#define DYNAMIC_ENGINE
-#define TEST_ENGINE_ID "javacoretests"
-#define TEST_ENGINE_NAME "libcore test engine"
-
-struct RSA_Delete {
- void operator()(RSA* p) const {
- RSA_free(p);
- }
-};
-typedef UniquePtr<RSA, RSA_Delete> Unique_RSA;
-
-static const char* HMAC_TAG = "-HMAC-";
-static const size_t HMAC_TAG_LEN = strlen(HMAC_TAG);
-
-static EVP_PKEY *test_load_key(ENGINE* e, const char *key_id,
- EVP_PKEY* (*read_func)(BIO*, EVP_PKEY**, pem_password_cb*, void*)) {
- void* data = static_cast<void*>(const_cast<char*>(key_id));
-
- EVP_PKEY *key = NULL;
-
- const size_t key_len = strlen(key_id);
- if (key_len > HMAC_TAG_LEN && !strncmp(key_id, HMAC_TAG, HMAC_TAG_LEN)) {
- key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, e, reinterpret_cast<const unsigned char*>(key_id),
- key_len);
- } else {
- BIO* in = BIO_new_mem_buf(data, strlen(key_id));
- if (!in) {
- return NULL;
- }
- key = read_func(in, NULL, 0, NULL);
- BIO_free(in);
-
- if (key != NULL && EVP_PKEY_type(key->type) == EVP_PKEY_RSA) {
- ENGINE_init(e);
-
- Unique_RSA rsa(EVP_PKEY_get1_RSA(key));
- rsa->engine = e;
- rsa->flags |= RSA_FLAG_EXT_PKEY;
- }
- }
-
- return key;
-}
-
-static EVP_PKEY* test_load_privkey(ENGINE* e, const char* key_id, UI_METHOD*, void*) {
- return test_load_key(e, key_id, PEM_read_bio_PrivateKey);
-}
-
-static EVP_PKEY* test_load_pubkey(ENGINE* e, const char* key_id, UI_METHOD*, void*) {
- return test_load_key(e, key_id, PEM_read_bio_PUBKEY);
-}
-
-static const int meths[] = {
- EVP_PKEY_HMAC,
-};
-
-static int pkey_meths(ENGINE*, EVP_PKEY_METHOD** meth, const int** nids, int nid) {
- if (nid == EVP_PKEY_HMAC) {
- *meth = const_cast<EVP_PKEY_METHOD*>(EVP_PKEY_meth_find(nid));
- return 1;
- } else if (nid != 0) {
- return 0;
- }
-
- if (nids != NULL) {
- *nids = meths;
- return 1;
- }
-
- return 0;
-}
-
-static int test_engine_setup(ENGINE* e) {
- if (!ENGINE_set_id(e, TEST_ENGINE_ID)
- || !ENGINE_set_name(e, TEST_ENGINE_NAME)
- || !ENGINE_set_flags(e, 0)
- || !ENGINE_set_RSA(e, RSA_get_default_method())
- || !ENGINE_set_load_privkey_function(e, test_load_privkey)
- || !ENGINE_set_load_pubkey_function(e, test_load_pubkey)
- || !ENGINE_set_pkey_meths(e, pkey_meths)) {
- return 0;
- }
-
- return 1;
-}
-
-static int test_engine_bind_fn(ENGINE *e, const char *id) {
- if (id && (strcmp(id, TEST_ENGINE_ID) != 0)) {
- return 0;
- }
-
- if (!test_engine_setup(e)) {
- return 0;
- }
-
- return 1;
-}
-
-extern "C" {
-#undef OPENSSL_EXPORT
-#define OPENSSL_EXPORT extern __attribute__ ((visibility ("default")))
-
-IMPLEMENT_DYNAMIC_CHECK_FN()
-IMPLEMENT_DYNAMIC_BIND_FN(test_engine_bind_fn)
-};
diff --git a/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
new file mode 100644
index 0000000..1a53312
--- /dev/null
+++ b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
@@ -0,0 +1 @@
+libcore.java.nio.charset.SettableCharsetProvider