|
27 | 27 | import com.google.cloud.storage.DefaultBufferedWritableByteChannelTest.AuditingBufferHandle;
|
28 | 28 | import com.google.cloud.storage.DefaultBufferedWritableByteChannelTest.CountingWritableByteChannelAdapter;
|
29 | 29 | import com.google.cloud.storage.UnbufferedWritableByteChannelSession.UnbufferedWritableByteChannel;
|
| 30 | +import com.google.cloud.storage.it.ChecksummedTestContent; |
30 | 31 | import com.google.common.collect.ImmutableList;
|
31 | 32 | import java.io.ByteArrayOutputStream;
|
32 | 33 | import java.io.IOException;
|
|
52 | 53 | import net.jqwik.api.Provide;
|
53 | 54 | import net.jqwik.api.providers.TypeUsage;
|
54 | 55 | import org.checkerframework.checker.nullness.qual.NonNull;
|
| 56 | +import org.slf4j.Logger; |
| 57 | +import org.slf4j.LoggerFactory; |
| 58 | +import org.slf4j.Marker; |
| 59 | +import org.slf4j.MarkerFactory; |
55 | 60 |
|
56 | 61 | public final class MinFlushBufferedWritableByteChannelTest {
|
| 62 | + private static final Logger LOGGER = |
| 63 | + LoggerFactory.getLogger(MinFlushBufferedWritableByteChannelTest.class); |
| 64 | + private static final Marker TRACE_ENTER = MarkerFactory.getMarker("enter"); |
| 65 | + private static final Marker TRACE_EXIT = MarkerFactory.getMarker("exit"); |
57 | 66 |
|
58 | 67 | @Example
|
59 | 68 | void edgeCases() {
|
60 | 69 | JqwikTest.report(TypeUsage.of(WriteOps.class), arbitraryWriteOps());
|
61 | 70 | }
|
62 | 71 |
|
| 72 | + @Example |
| 73 | + void nonBlockingWrite0DoesNotBlock() throws IOException { |
| 74 | + BufferHandle handle = BufferHandle.allocate(5); |
| 75 | + MinFlushBufferedWritableByteChannel c = |
| 76 | + new MinFlushBufferedWritableByteChannel(handle, new OnlyConsumeNBytes(0, 1), false); |
| 77 | + |
| 78 | + ChecksummedTestContent all = ChecksummedTestContent.gen(11); |
| 79 | + ByteBuffer s_0_4 = ByteBuffer.wrap(all.slice(0, 4).getBytes()); |
| 80 | + ByteBuffer s_4_4 = ByteBuffer.wrap(all.slice(0, 4).getBytes()); |
| 81 | + ByteBuffer s_8_3 = ByteBuffer.wrap(all.slice(0, 3).getBytes()); |
| 82 | + int written1 = c.write(s_0_4); |
| 83 | + assertThat(written1).isEqualTo(4); |
| 84 | + assertThat(s_0_4.remaining()).isEqualTo(0); |
| 85 | + |
| 86 | + int written2 = c.write(s_4_4); |
| 87 | + assertThat(written2).isEqualTo(0); |
| 88 | + assertThat(s_4_4.remaining()).isEqualTo(4); |
| 89 | + |
| 90 | + int written3 = c.write(s_8_3); |
| 91 | + assertThat(written3).isEqualTo(0); |
| 92 | + assertThat(s_8_3.remaining()).isEqualTo(3); |
| 93 | + |
| 94 | + assertThat(handle.remaining()).isEqualTo(1); |
| 95 | + } |
| 96 | + |
| 97 | + @Example |
| 98 | + void nonBlockingWritePartialDoesNotBlock() throws IOException { |
| 99 | + BufferHandle handle = BufferHandle.allocate(5); |
| 100 | + MinFlushBufferedWritableByteChannel c = |
| 101 | + new MinFlushBufferedWritableByteChannel(handle, new OnlyConsumeNBytes(6, 5), false); |
| 102 | + |
| 103 | + ChecksummedTestContent all = ChecksummedTestContent.gen(11); |
| 104 | + ByteBuffer s_0_4 = ByteBuffer.wrap(all.slice(0, 4).getBytes()); |
| 105 | + ByteBuffer s_4_4 = ByteBuffer.wrap(all.slice(0, 4).getBytes()); |
| 106 | + int written1 = c.write(s_0_4); |
| 107 | + assertThat(written1).isEqualTo(4); |
| 108 | + assertThat(s_0_4.remaining()).isEqualTo(0); |
| 109 | + assertThat(handle.remaining()).isEqualTo(1); |
| 110 | + |
| 111 | + int written2 = c.write(s_4_4); |
| 112 | + assertThat(written2).isEqualTo(1); |
| 113 | + assertThat(s_4_4.remaining()).isEqualTo(3); |
| 114 | + assertThat(handle.remaining()).isEqualTo(5); |
| 115 | + } |
| 116 | + |
| 117 | + @Example |
| 118 | + void illegalStateExceptionIfWrittenLt0() throws IOException { |
| 119 | + BufferHandle handle = BufferHandle.allocate(4); |
| 120 | + MinFlushBufferedWritableByteChannel c = |
| 121 | + new MinFlushBufferedWritableByteChannel( |
| 122 | + handle, |
| 123 | + new UnbufferedWritableByteChannel() { |
| 124 | + @Override |
| 125 | + public long write(ByteBuffer[] srcs, int offset, int length) { |
| 126 | + return -1; |
| 127 | + } |
| 128 | + |
| 129 | + @Override |
| 130 | + public boolean isOpen() { |
| 131 | + return true; |
| 132 | + } |
| 133 | + |
| 134 | + @Override |
| 135 | + public void close() {} |
| 136 | + }); |
| 137 | + |
| 138 | + ChecksummedTestContent all = ChecksummedTestContent.gen(11); |
| 139 | + ByteBuffer s_0_4 = ByteBuffer.wrap(all.slice(0, 4).getBytes()); |
| 140 | + assertThrows(IllegalStateException.class, () -> c.write(s_0_4)); |
| 141 | + } |
| 142 | + |
63 | 143 | @Property
|
64 | 144 | void bufferingEagerlyFlushesWhenFull(@ForAll("WriteOps") WriteOps writeOps) throws IOException {
|
65 | 145 | ByteBuffer buffer = ByteBuffer.allocate(writeOps.bufferSize);
|
@@ -580,4 +660,51 @@ static WriteOps of(int numBytes, int bufferSize, int writeSize) {
|
580 | 660 | dbgExpectedWriteSizes);
|
581 | 661 | }
|
582 | 662 | }
|
| 663 | + |
| 664 | + private static final class OnlyConsumeNBytes implements UnbufferedWritableByteChannel { |
| 665 | + private static final Logger LOGGER = LoggerFactory.getLogger(OnlyConsumeNBytes.class); |
| 666 | + private final long bytesToConsume; |
| 667 | + private final int consumptionIncrement; |
| 668 | + private long bytesConsumed; |
| 669 | + |
| 670 | + private OnlyConsumeNBytes(int bytesToConsume, int consumptionIncrement) { |
| 671 | + this.bytesToConsume = bytesToConsume; |
| 672 | + this.consumptionIncrement = consumptionIncrement; |
| 673 | + this.bytesConsumed = 0; |
| 674 | + } |
| 675 | + |
| 676 | + @Override |
| 677 | + public long write(ByteBuffer[] srcs, int offset, int length) { |
| 678 | + LOGGER.info(TRACE_ENTER, "write(srcs : {}, offset : {}, length : {})", srcs, offset, length); |
| 679 | + try { |
| 680 | + if (bytesConsumed >= bytesToConsume) { |
| 681 | + return 0; |
| 682 | + } |
| 683 | + |
| 684 | + long consumed = 0; |
| 685 | + int toConsume = consumptionIncrement; |
| 686 | + for (int i = offset; i < length && toConsume > 0; i++) { |
| 687 | + ByteBuffer src = srcs[i]; |
| 688 | + int remaining = src.remaining(); |
| 689 | + int position = src.position(); |
| 690 | + int consumable = Math.min(toConsume, remaining); |
| 691 | + toConsume -= consumable; |
| 692 | + consumed += consumable; |
| 693 | + src.position(position + consumable); |
| 694 | + } |
| 695 | + bytesConsumed += consumed; |
| 696 | + return consumed; |
| 697 | + } finally { |
| 698 | + LOGGER.info(TRACE_EXIT, "write(srcs : {}, offset : {}, length : {})", srcs, offset, length); |
| 699 | + } |
| 700 | + } |
| 701 | + |
| 702 | + @Override |
| 703 | + public boolean isOpen() { |
| 704 | + return true; |
| 705 | + } |
| 706 | + |
| 707 | + @Override |
| 708 | + public void close() {} |
| 709 | + } |
583 | 710 | }
|
0 commit comments