| /* |
| * Copyright (C) 2018 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 okio |
| |
| import org.assertj.core.api.Assertions.assertThat |
| import org.junit.Test |
| import java.util.concurrent.TimeUnit |
| |
| class ThrottlerTakeTest { |
| private var nowNanos = 0L |
| private var elapsedNanos = 0L |
| private val throttler = Throttler(allocatedUntil = nowNanos) |
| |
| @Test fun takeByByteCount() { |
| throttler.bytesPerSecond(bytesPerSecond = 20, waitByteCount = 5, maxByteCount = 10) |
| |
| // We get the first 10 bytes immediately (that's maxByteCount). |
| assertThat(take(100L)).isEqualTo(10L) |
| assertElapsed(0L) |
| |
| // Wait a quarter second for each subsequent 5 bytes (that's waitByteCount). |
| assertThat(take(100L)).isEqualTo(5L) |
| assertElapsed(250L) |
| |
| assertThat(take(100L)).isEqualTo(5L) |
| assertElapsed(250L) |
| |
| // Wait three quarters of a second to build up 15 bytes of potential. |
| // Since maxByteCount = 10, there will only be 10 bytes of potential. |
| sleep(750L) |
| assertElapsed(750L) |
| |
| // We get 10 bytes immediately (that's maxByteCount again). |
| assertThat(take(100L)).isEqualTo(10L) |
| assertElapsed(0L) |
| |
| // Wait a quarter second for each subsequent 5 bytes (that's waitByteCount again). |
| assertThat(take(100L)).isEqualTo(5L) |
| assertElapsed(250L) |
| } |
| |
| @Test fun takeFullyTimeElapsed() { |
| throttler.bytesPerSecond(bytesPerSecond = 20, waitByteCount = 5, maxByteCount = 10) |
| |
| // We write the first 10 bytes immediately (that's maxByteCount again). |
| takeFully(10L) |
| assertElapsed(0L) |
| |
| // Wait a quarter second for each subsequent 5 bytes (that's waitByteCount). |
| takeFully(5L) |
| assertElapsed(250L) |
| |
| // Wait a half second for 10 bytes. |
| takeFully(10L) |
| assertElapsed(500L) |
| |
| // Wait a three quarters of a second to build up 15 bytes of potential. |
| // Since maxByteCount = 10, there will only be 10 bytes of potential. |
| sleep(750L) |
| assertElapsed(750L) |
| |
| // We write the first 10 bytes immediately (that's maxByteCount again). |
| // Wait a quarter second for each subsequent 5 bytes (that's waitByteCount again). |
| takeFully(15L) |
| assertElapsed(250L) |
| } |
| |
| @Test fun takeFullyWhenSaturated() { |
| throttler.bytesPerSecond(400L, 5L, 10L) |
| |
| // Saturate the throttler. |
| assertThat(take(10L)).isEqualTo(10L) |
| assertElapsed(0L) |
| |
| // At 400 bytes per second it takes 250 ms to read 100 bytes. |
| takeFully(100L) |
| assertElapsed(250L) |
| } |
| |
| @Test fun takeFullyNoLimit() { |
| throttler.bytesPerSecond(0L, 5L, 10L) |
| takeFully(100L) |
| assertElapsed(0L) |
| } |
| |
| /** |
| * We had a bug where integer division truncation would cause us to call wait() for 0 nanos. We |
| * fixed it by minimizing integer division generally, and by handling that case specifically. |
| */ |
| @Test fun infiniteWait() { |
| throttler.bytesPerSecond(3, maxByteCount = 4, waitByteCount = 4) |
| takeFully(7) |
| assertElapsed(1000L) |
| } |
| |
| /** Take at least the minimum and up to `byteCount` bytes, sleeping once if necessary. */ |
| private fun take(byteCount: Long): Long { |
| val byteCountOrWaitNanos = throttler.byteCountOrWaitNanos(nowNanos, byteCount) |
| if (byteCountOrWaitNanos >= 0L) return byteCountOrWaitNanos |
| |
| nowNanos += -byteCountOrWaitNanos |
| |
| val resultAfterWait = throttler.byteCountOrWaitNanos(nowNanos, byteCount) |
| assertThat(resultAfterWait).isGreaterThan(0L) |
| return resultAfterWait |
| } |
| |
| /** Take all of `byteCount` bytes, advancing the clock until they're all taken. */ |
| private fun takeFully(byteCount: Long) { |
| var remaining = byteCount |
| while (remaining > 0L) { |
| val byteCountOrWaitNanos = throttler.byteCountOrWaitNanos(nowNanos, remaining) |
| if (byteCountOrWaitNanos >= 0L) { |
| remaining -= byteCountOrWaitNanos |
| } else { |
| nowNanos += -byteCountOrWaitNanos |
| } |
| } |
| } |
| |
| private fun assertElapsed(millis: Long) { |
| elapsedNanos += TimeUnit.MILLISECONDS.toNanos(millis) |
| assertThat(nowNanos).isEqualTo(elapsedNanos) |
| } |
| |
| private fun sleep(millis: Long) { |
| nowNanos += TimeUnit.MILLISECONDS.toNanos(millis) |
| } |
| } |