Merge "Late binding: add tests for init-time rejection"
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index dce0e62..5c46f03 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -1007,6 +1007,94 @@
         }
     }
 
+    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_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);
diff --git a/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java b/luni/src/test/java/libcore/javax/crypto/MockCipherSpi.java
index 6742cf3..e398b33 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.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,71 @@
     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 {
+        }
+
+        @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 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 {
     }