public static void testEncryptionAndDecryption(
      StreamingAead encryptionStreamingAead,
      StreamingAead decryptionStreamingAead,
      byte[] plaintext,
      byte[] associatedData)
      throws Exception {

    // Encrypt plaintext.
    ByteArrayOutputStream ciphertext = new ByteArrayOutputStream();
    try (WritableByteChannel encChannel =
        encryptionStreamingAead.newEncryptingChannel(
            Channels.newChannel(ciphertext), associatedData)) {
      encChannel.write(ByteBuffer.wrap(plaintext));
    }

    // Decrypt ciphertext via ReadableByteChannel.
    {
      ByteBufferChannel ciphertextChannel = new ByteBufferChannel(ciphertext.toByteArray());
      ReadableByteChannel decChannel =
          decryptionStreamingAead.newDecryptingChannel(ciphertextChannel, associatedData);
      ByteBuffer decrypted = ByteBuffer.allocate(plaintext.length);
      int unused = decChannel.read(decrypted);

      // Compare results;
      TestUtil.assertByteArrayEquals(plaintext, decrypted.array());
    }

    // Decrypt ciphertext via ReadableByteChannel, using a very small chunck size.
    {
      ByteBufferChannel ciphertextChannel = new ByteBufferChannel(
          ciphertext.toByteArray(), /* */10, true);
      ReadableByteChannel decChannel =
          decryptionStreamingAead.newDecryptingChannel(ciphertextChannel, associatedData);
      ByteBuffer decrypted = ByteBuffer.allocate(plaintext.length);
      do {
        int unused = decChannel.read(decrypted);
      } while (decrypted.hasRemaining());
      // Compare results;
      TestUtil.assertByteArrayEquals(plaintext, decrypted.array());
    }

    // Decrypt ciphertext via SeekableByteChannel.
    {
      SeekableByteChannel ciphertextChannel =
          new SeekableByteBufferChannel(ciphertext.toByteArray());
      SeekableByteChannel decChannel =
          decryptionStreamingAead.newSeekableDecryptingChannel(ciphertextChannel, associatedData);
      ByteBuffer decrypted = ByteBuffer.allocate(plaintext.length);
      int unused = decChannel.read(decrypted);

      // Compare results;
      TestUtil.assertByteArrayEquals(plaintext, decrypted.array());
    }

    // Decrypt ciphertext via SeekableByteChannel, using a very small chunck size.
    {
      SeekableByteChannel ciphertextChannel =
          new SeekableByteBufferChannel(ciphertext.toByteArray(), 10);
      SeekableByteChannel decChannel =
          decryptionStreamingAead.newSeekableDecryptingChannel(ciphertextChannel, associatedData);
      ByteBuffer decrypted = ByteBuffer.allocate(plaintext.length);
      do {
        int unused = decChannel.read(decrypted);
      } while (decrypted.hasRemaining());
      // Compare results;
      TestUtil.assertByteArrayEquals(plaintext, decrypted.array());
    }

    // Decrypt ciphertext via SeekableByteChannel, setting position
    if (plaintext.length > 5) {
      SeekableByteChannel ciphertextChannel =
          new SeekableByteBufferChannel(ciphertext.toByteArray(), 10);
      SeekableByteChannel decChannel =
          decryptionStreamingAead.newSeekableDecryptingChannel(ciphertextChannel, associatedData);
      decChannel.position(5);
      assertEquals(5, decChannel.position());

      ByteBuffer decrypted = ByteBuffer.allocate(plaintext.length - 5);
      do {
        int unused = decChannel.read(decrypted);
      } while (decrypted.hasRemaining());
      // Compare results;
      TestUtil.assertByteArrayEquals(
          Arrays.copyOfRange(plaintext, 5, plaintext.length),
          decrypted.array());
    }

    // Decrypt ciphertext via InputStream using read(byte[])
    {
      InputStream ctStream = new ByteArrayInputStream(ciphertext.toByteArray());
      InputStream decStream = decryptionStreamingAead.newDecryptingStream(ctStream, associatedData);
      byte[] decrypted = new byte[plaintext.length];
      int decryptedLength = decStream.read(decrypted);

      assertEquals("Decrypted length should be equal to plaintext length", decryptedLength,
          plaintext.length);
      TestUtil.assertByteArrayEquals(plaintext, decrypted);

      byte[] buf = new byte[1];
      int n = decStream.read(buf);
      assertThat(n).isEqualTo(-1);
    }

    // Decrypt ciphertext via InputStream using read()
    {
      InputStream ctStream = new ByteArrayInputStream(ciphertext.toByteArray());
      InputStream decStream = decryptionStreamingAead.newDecryptingStream(ctStream, associatedData);
      byte[] decrypted = new byte[plaintext.length];
      for (int i = 0; i < plaintext.length; i++) {
        int b = decStream.read();
        assertThat(b).isAtLeast(0);
        assertThat(b).isAtMost(255);
        decrypted[i] = (byte) b;
      }
      assertThat(decrypted).isEqualTo(plaintext);

      int b = decStream.read();
      assertThat(b).isEqualTo(-1);
    }

    // Decrypt ciphertext via InputStream using read(byte[], int, int)
    {
      InputStream ctStream = new ByteArrayInputStream(ciphertext.toByteArray());
      InputStream decStream = decryptionStreamingAead.newDecryptingStream(ctStream, associatedData);
      byte[] decrypted = new byte[plaintext.length];
      for (int i = 0; i < plaintext.length; i++) {
        int n = decStream.read(decrypted, i, 1);
        assertThat(n).isEqualTo(1);
      }
      assertThat(decrypted).isEqualTo(plaintext);

      byte[] buf = new byte[1];
      int n = decStream.read(buf, 0, 1);
      assertThat(n).isEqualTo(-1);
    }

    // Decrypt ciphertext via SmallChunksByteArrayInputStream.
    {
      InputStream ctStream = new SmallChunksByteArrayInputStream(ciphertext.toByteArray(), 10);
      InputStream decStream = decryptionStreamingAead.newDecryptingStream(ctStream, associatedData);
      byte[] decrypted = new byte[plaintext.length];
      int decryptedLength = decStream.read(decrypted);

      // Compare results;
      assertEquals("Decrypted length should be equal to plaintext length", decryptedLength,
          plaintext.length);
      TestUtil.assertByteArrayEquals(plaintext, decrypted);
    }

    // Encrypt with an OutputStream.
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try (OutputStream encStream =
        encryptionStreamingAead.newEncryptingStream(bos, associatedData)) {
      encStream.write(plaintext);
    }
    byte[] ciphertext2 = bos.toByteArray();

    // Check that the stream encrypted ciphertext is correct.
    {
      ByteBufferChannel ciphertextChannel = new ByteBufferChannel(ciphertext2);
      ReadableByteChannel decChannel =
          decryptionStreamingAead.newDecryptingChannel(ciphertextChannel, associatedData);
      ByteBuffer decrypted = ByteBuffer.allocate(plaintext.length);
      int unused = decChannel.read(decrypted);

      // Compare results;
      TestUtil.assertByteArrayEquals(plaintext, decrypted.array());
    }

  }
Tests encryption and decryption functionalities using {@code encryptionStreamingAead} for
encryption and {@code decryptionStreamingAead} for decryption on inputs {@code plaintext} and
{@code associatedData}.
java_src/src/main/java/com/google/crypto/tink/testing/StreamingTestUtil.java