/*
 * Copyright (C) 2014 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.okhttp.internal.spdy;

import com.squareup.okhttp.internal.Util;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import okio.ByteString;
import okio.OkBuffer;
import org.junit.Test;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

public class Spdy3Test {
  static final int expectedStreamId = 15;

  @Test public void tooLargeDataFrame() throws IOException {
    try {
      sendDataFrame(new OkBuffer().write(new byte[0x1000000]));
      fail();
    } catch (IllegalArgumentException e) {
      assertEquals("FRAME_TOO_LARGE max size is 16Mib: " + 0x1000000L, e.getMessage());
    }
  }

  @Test public void badWindowSizeIncrement() throws IOException {
    try {
      windowUpdate(0);
      fail();
    } catch (IllegalArgumentException e) {
      assertEquals("windowSizeIncrement must be between 1 and 0x7fffffff: 0", e.getMessage());
    }
    try {
      windowUpdate(0x80000000L);
      fail();
    } catch (IllegalArgumentException e) {
      assertEquals("windowSizeIncrement must be between 1 and 0x7fffffff: 2147483648",
          e.getMessage());
    }
  }

  @Test public void goAwayRoundTrip() throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    DataOutputStream dataOut = new DataOutputStream(out);

    final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;

    // Compose the expected GOAWAY frame without debug data
    // |C| Version(15bits) | Type(16bits) |
    dataOut.writeInt(0x80000000 | (Spdy3.VERSION & 0x7fff) << 16 | Spdy3.TYPE_GOAWAY & 0xffff);
    // | Flags (8)  |  Length (24 bits)   |
    dataOut.writeInt(8); // no flags and length is 8.
    dataOut.writeInt(expectedStreamId); // last good stream.
    dataOut.writeInt(expectedError.spdyGoAwayCode);

    // Check writer sends the same bytes.
    assertArrayEquals(out.toByteArray(),
        sendGoAway(expectedStreamId, expectedError, Util.EMPTY_BYTE_ARRAY));

    // SPDY/3 does not send debug data, so bytes should be same!
    assertArrayEquals(out.toByteArray(), sendGoAway(expectedStreamId, expectedError, new byte[8]));

    FrameReader fr = newReader(out);

    fr.nextFrame(new BaseTestHandler() { // Consume the goAway frame.
      @Override public void goAway(
          int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
        assertEquals(expectedStreamId, lastGoodStreamId);
        assertEquals(expectedError, errorCode);
        assertEquals(0, debugData.size());
      }
    });
  }

  private Spdy3.Reader newReader(ByteArrayOutputStream out) {
    OkBuffer data = new OkBuffer().write(out.toByteArray());
    return new Spdy3.Reader(data, false);
  }

  private byte[] sendDataFrame(OkBuffer source) throws IOException {
    OkBuffer out = new OkBuffer();
    Spdy3.Writer writer = new Spdy3.Writer(out, true);
    writer.sendDataFrame(expectedStreamId, 0, source, (int) source.byteCount());
    return out.readByteString((int) out.byteCount()).toByteArray();
  }

  private byte[] windowUpdate(long increment) throws IOException {
    OkBuffer out = new OkBuffer();
    new Spdy3.Writer(out, true).windowUpdate(expectedStreamId, increment);
    return out.readByteString((int) out.byteCount()).toByteArray();
  }

  private byte[] sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData)
      throws IOException {
    OkBuffer out = new OkBuffer();
    new Spdy3.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData);
    return out.readByteString((int) out.byteCount()).toByteArray();
  }
}

@@ -684,79 +684,8 @@
 il;%0A
-import java.io.ByteArrayOutputStream;%0Aimport java.io.DataOutputStream;%0A
 impo
@@ -782,58 +782,8 @@
 t;%0A%0A
-import static org.junit.Assert.assertArrayEquals;%0A
 impo
@@ -1769,120 +1769,38 @@
     
-ByteArrayOutputStream out = new ByteArrayOutputStream();%0A    DataOutputStream dataOut = new DataOutputStream(out
+OkBuffer frame = new OkBuffer(
 );%0A%0A
@@ -1966,31 +1966,29 @@
 bits) %7C%0A    
-dataOut
+frame
 .writeInt(0x
@@ -2104,31 +2104,29 @@
 ts)   %7C%0A    
-dataOut
+frame
 .writeInt(8)
@@ -2156,31 +2156,29 @@
 h is 8.%0A    
-dataOut
+frame
 .writeInt(ex
@@ -2223,15 +2223,13 @@
     
-dataOut
+frame
 .wri
@@ -2322,46 +2322,21 @@
 sert
-Array
 Equals(
-out.toByteArray(),%0A       
+frame,
  sen
@@ -2481,37 +2481,20 @@
 sert
-Array
 Equals(
-out.toByteArray()
+frame
 , se
@@ -2579,18 +2579,34 @@
  new
-Reader(out
+ Spdy3.Reader(frame, false
 );%0A%0A
@@ -2972,184 +2972,12 @@
 ate 
-Spdy3.Reader newReader(ByteArrayOutputStream out) %7B%0A    OkBuffer data = new OkBuffer().write(out.toByteArray());%0A    return new Spdy3.Reader(data, false);%0A  %7D%0A%0A  private byte%5B%5D
+void
  sen
@@ -3029,43 +3029,8 @@
 n %7B%0A
-    OkBuffer out = new OkBuffer();%0A
     
@@ -3064,27 +3064,38 @@
 pdy3.Writer(
-out
+new OkBuffer()
 , true);%0A   
@@ -3176,76 +3176,8 @@
 ));%0A
-    return out.readByteString((int) out.byteCount()).toByteArray();%0A
   %7D%0A
@@ -3187,22 +3187,20 @@
 private 
-byte%5B%5D
+void
  windowU
@@ -3246,43 +3246,8 @@
 n %7B%0A
-    OkBuffer out = new OkBuffer();%0A
     
@@ -3255,35 +3255,46 @@
 ew Spdy3.Writer(
-out
+new OkBuffer()
 , true).windowUp
@@ -3332,76 +3332,8 @@
 t);%0A
-    return out.readByteString((int) out.byteCount()).toByteArray();%0A
   %7D%0A
@@ -3343,22 +3343,24 @@
 private 
-byte%5B%5D
+OkBuffer
  sendGoA
@@ -3584,60 +3584,8 @@
  out
-.readByteString((int) out.byteCount()).toByteArray()
 ;%0A  

Use OkBuffer in Spdy3Test.