void test_nghttp2_session_set_local_window_size(void) {
  nghttp2_session *session;
  nghttp2_session_callbacks callbacks;
  nghttp2_outbound_item *item;
  nghttp2_stream *stream;

  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
  callbacks.send_callback = null_send_callback;

  nghttp2_session_client_new(&session, &callbacks, NULL);
  stream = open_sent_stream(session, 1);
  stream->recv_window_size = 4096;

  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 1, 65536));
  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
            stream->local_window_size);
  CU_ASSERT(4096 == stream->recv_window_size);
  CU_ASSERT(65536 - 4096 ==
            nghttp2_session_get_stream_local_window_size(session, 1));

  item = nghttp2_session_get_next_ob_item(session);

  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
  CU_ASSERT(1 == item->frame.window_update.hd.stream_id);
  CU_ASSERT(1 == item->frame.window_update.window_size_increment);

  CU_ASSERT(0 == nghttp2_session_send(session));

  /* Go decrement part */
  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 1, 32768));
  CU_ASSERT(32768 == stream->local_window_size);
  CU_ASSERT(-28672 == stream->recv_window_size);
  CU_ASSERT(32768 == stream->recv_reduction);
  CU_ASSERT(65536 - 4096 ==
            nghttp2_session_get_stream_local_window_size(session, 1));

  item = nghttp2_session_get_next_ob_item(session);

  CU_ASSERT(item == NULL);

  /* Increase local window size */
  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 1, 49152));
  CU_ASSERT(49152 == stream->local_window_size);
  CU_ASSERT(-12288 == stream->recv_window_size);
  CU_ASSERT(16384 == stream->recv_reduction);
  CU_ASSERT(65536 - 4096 ==
            nghttp2_session_get_stream_local_window_size(session, 1));
  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));

  /* Increase local window again */
  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 1, 65537));
  CU_ASSERT(65537 == stream->local_window_size);
  CU_ASSERT(4096 == stream->recv_window_size);
  CU_ASSERT(0 == stream->recv_reduction);
  CU_ASSERT(65537 - 4096 ==
            nghttp2_session_get_stream_local_window_size(session, 1));

  item = nghttp2_session_get_next_ob_item(session);

  CU_ASSERT(1 == item->frame.window_update.window_size_increment);

  CU_ASSERT(0 == nghttp2_session_send(session));

  /* Check connection-level flow control */
  session->recv_window_size = 4096;
  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 0, 65536));
  CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
            session->local_window_size);
  CU_ASSERT(4096 == session->recv_window_size);
  CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session));

  item = nghttp2_session_get_next_ob_item(session);

  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
  CU_ASSERT(0 == item->frame.window_update.hd.stream_id);
  CU_ASSERT(1 == item->frame.window_update.window_size_increment);

  CU_ASSERT(0 == nghttp2_session_send(session));

  /* Go decrement part */
  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 0, 32768));
  CU_ASSERT(32768 == session->local_window_size);
  CU_ASSERT(-28672 == session->recv_window_size);
  CU_ASSERT(32768 == session->recv_reduction);
  CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session));

  item = nghttp2_session_get_next_ob_item(session);

  CU_ASSERT(item == NULL);

  /* Increase local window size */
  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 0, 49152));
  CU_ASSERT(49152 == session->local_window_size);
  CU_ASSERT(-12288 == session->recv_window_size);
  CU_ASSERT(16384 == session->recv_reduction);
  CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session));
  CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));

  /* Increase local window again */
  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 0, 65537));
  CU_ASSERT(65537 == session->local_window_size);
  CU_ASSERT(4096 == session->recv_window_size);
  CU_ASSERT(0 == session->recv_reduction);
  CU_ASSERT(65537 - 4096 == nghttp2_session_get_local_window_size(session));

  item = nghttp2_session_get_next_ob_item(session);

  CU_ASSERT(1 == item->frame.window_update.window_size_increment);

  CU_ASSERT(0 == nghttp2_session_send(session));

  nghttp2_session_del(session);

  /* Make sure that nghttp2_session_set_local_window_size submits
     WINDOW_UPDATE if necessary to increase stream-level window. */
  nghttp2_session_client_new(&session, &callbacks, NULL);
  stream = open_sent_stream(session, 1);
  stream->recv_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;

  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 1, 0));
  CU_ASSERT(0 == stream->recv_window_size);
  CU_ASSERT(0 == nghttp2_session_get_stream_local_window_size(session, 1));
  /* This should submit WINDOW_UPDATE frame because stream-level
     receiving window is now full. */
  CU_ASSERT(0 ==
            nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 1,
                                                  NGHTTP2_INITIAL_WINDOW_SIZE));
  CU_ASSERT(0 == stream->recv_window_size);
  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
            nghttp2_session_get_stream_local_window_size(session, 1));

  item = nghttp2_session_get_next_ob_item(session);

  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
  CU_ASSERT(1 == item->frame.hd.stream_id);
  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
            item->frame.window_update.window_size_increment);

  nghttp2_session_del(session);

  /* Make sure that nghttp2_session_set_local_window_size submits
     WINDOW_UPDATE if necessary to increase connection-level
     window. */
  nghttp2_session_client_new(&session, &callbacks, NULL);
  session->recv_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;

  CU_ASSERT(0 == nghttp2_session_set_local_window_size(
                     session, NGHTTP2_FLAG_NONE, 0, 0));
  CU_ASSERT(0 == session->recv_window_size);
  CU_ASSERT(0 == nghttp2_session_get_local_window_size(session));
  /* This should submit WINDOW_UPDATE frame because connection-level
     receiving window is now full. */
  CU_ASSERT(0 ==
            nghttp2_session_set_local_window_size(session, NGHTTP2_FLAG_NONE, 0,
                                                  NGHTTP2_INITIAL_WINDOW_SIZE));
  CU_ASSERT(0 == session->recv_window_size);
  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
            nghttp2_session_get_local_window_size(session));

  item = nghttp2_session_get_next_ob_item(session);

  CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
  CU_ASSERT(0 == item->frame.hd.stream_id);
  CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE ==
            item->frame.window_update.window_size_increment);

  nghttp2_session_del(session);
}