|
27 | 27 | import com.google.cloud.storage.Crc32cValue.Crc32cLengthKnown; |
28 | 28 | import com.google.cloud.storage.it.ChecksummedTestContent; |
29 | 29 | import com.google.cloud.storage.it.GrpcPlainRequestLoggingInterceptor; |
| 30 | +import com.google.common.collect.ImmutableMap; |
30 | 31 | import com.google.protobuf.Any; |
31 | 32 | import com.google.protobuf.ByteString; |
32 | 33 | import com.google.storage.v2.BidiReadHandle; |
@@ -638,6 +639,186 @@ public void onCompleted() { |
638 | 639 | } |
639 | 640 | } |
640 | 641 |
|
| 642 | + @Test |
| 643 | + public void objectRangeData_offset_notAligned_lt() throws Exception { |
| 644 | + |
| 645 | + Object metadata = |
| 646 | + Object.newBuilder() |
| 647 | + .setBucket(BucketName.format("_", "b")) |
| 648 | + .setName("o") |
| 649 | + .setGeneration(1) |
| 650 | + .build(); |
| 651 | + byte[] b64bytes = DataGenerator.base64Characters().genBytes(64); |
| 652 | + ChecksummedTestContent expected = ChecksummedTestContent.of(b64bytes, 10, 20); |
| 653 | + |
| 654 | + BidiReadObjectRequest req1 = |
| 655 | + BidiReadObjectRequest.newBuilder() |
| 656 | + .setReadObjectSpec( |
| 657 | + BidiReadObjectSpec.newBuilder() |
| 658 | + .setBucket(metadata.getBucket()) |
| 659 | + .setObject(metadata.getName()) |
| 660 | + .build()) |
| 661 | + .build(); |
| 662 | + BidiReadObjectResponse res1 = BidiReadObjectResponse.newBuilder().setMetadata(metadata).build(); |
| 663 | + |
| 664 | + ChecksummedTestContent content2 = ChecksummedTestContent.of(b64bytes, 9, 20); |
| 665 | + BidiReadObjectRequest req2 = |
| 666 | + BidiReadObjectRequest.newBuilder().addReadRanges(getReadRange(1, 10, 20)).build(); |
| 667 | + BidiReadObjectResponse res2 = |
| 668 | + BidiReadObjectResponse.newBuilder() |
| 669 | + .addObjectDataRanges( |
| 670 | + ObjectRangeData.newBuilder() |
| 671 | + .setChecksummedData(content2.asChecksummedData()) |
| 672 | + .setReadRange(getReadRange(1, 9, content2)) |
| 673 | + .setRangeEnd(true) |
| 674 | + .build()) |
| 675 | + .build(); |
| 676 | + |
| 677 | + ChecksummedTestContent content3 = ChecksummedTestContent.of(b64bytes, 29, 1); |
| 678 | + BidiReadObjectRequest req3 = |
| 679 | + BidiReadObjectRequest.newBuilder().addReadRanges(getReadRange(2, 29, 1)).build(); |
| 680 | + BidiReadObjectResponse res3 = |
| 681 | + BidiReadObjectResponse.newBuilder() |
| 682 | + .setMetadata(metadata) |
| 683 | + .addObjectDataRanges( |
| 684 | + ObjectRangeData.newBuilder() |
| 685 | + .setChecksummedData(content3.asChecksummedData()) |
| 686 | + .setReadRange(getReadRange(2, 29, content3)) |
| 687 | + .setRangeEnd(true) |
| 688 | + .build()) |
| 689 | + .build(); |
| 690 | + |
| 691 | + ImmutableMap<BidiReadObjectRequest, BidiReadObjectResponse> db = |
| 692 | + ImmutableMap.<BidiReadObjectRequest, BidiReadObjectResponse>builder() |
| 693 | + .put(req1, res1) |
| 694 | + .put(req2, res2) |
| 695 | + .put(req3, res3) |
| 696 | + .buildOrThrow(); |
| 697 | + |
| 698 | + runTestAgainstFakeServer(expected, db, ByteRangeSpec.relativeLength(10L, 20L)); |
| 699 | + } |
| 700 | + |
| 701 | + @Test |
| 702 | + public void objectRangeData_offset_notAligned_gt() throws Exception { |
| 703 | + |
| 704 | + Object metadata = |
| 705 | + Object.newBuilder() |
| 706 | + .setBucket(BucketName.format("_", "b")) |
| 707 | + .setName("o") |
| 708 | + .setGeneration(1) |
| 709 | + .build(); |
| 710 | + byte[] b64bytes = DataGenerator.base64Characters().genBytes(64); |
| 711 | + ChecksummedTestContent expected = ChecksummedTestContent.of(b64bytes, 10, 20); |
| 712 | + |
| 713 | + BidiReadObjectRequest req1 = |
| 714 | + BidiReadObjectRequest.newBuilder() |
| 715 | + .setReadObjectSpec( |
| 716 | + BidiReadObjectSpec.newBuilder() |
| 717 | + .setBucket(metadata.getBucket()) |
| 718 | + .setObject(metadata.getName()) |
| 719 | + .build()) |
| 720 | + .build(); |
| 721 | + BidiReadObjectResponse res1 = BidiReadObjectResponse.newBuilder().setMetadata(metadata).build(); |
| 722 | + |
| 723 | + ChecksummedTestContent content2 = ChecksummedTestContent.of(b64bytes, 11, 20); |
| 724 | + BidiReadObjectRequest req2 = |
| 725 | + BidiReadObjectRequest.newBuilder().addReadRanges(getReadRange(1, 10, 20)).build(); |
| 726 | + BidiReadObjectResponse res2 = |
| 727 | + BidiReadObjectResponse.newBuilder() |
| 728 | + .addObjectDataRanges( |
| 729 | + ObjectRangeData.newBuilder() |
| 730 | + .setChecksummedData(content2.asChecksummedData()) |
| 731 | + .setReadRange(getReadRange(1, 11, content2)) |
| 732 | + .setRangeEnd(true) |
| 733 | + .build()) |
| 734 | + .build(); |
| 735 | + |
| 736 | + ChecksummedTestContent content3 = ChecksummedTestContent.of(b64bytes, 10, 20); |
| 737 | + BidiReadObjectRequest req3 = |
| 738 | + BidiReadObjectRequest.newBuilder().addReadRanges(getReadRange(2, 10, 20)).build(); |
| 739 | + BidiReadObjectResponse res3 = |
| 740 | + BidiReadObjectResponse.newBuilder() |
| 741 | + .setMetadata(metadata) |
| 742 | + .addObjectDataRanges( |
| 743 | + ObjectRangeData.newBuilder() |
| 744 | + .setChecksummedData(content3.asChecksummedData()) |
| 745 | + .setReadRange(getReadRange(2, 10, content3)) |
| 746 | + .setRangeEnd(true) |
| 747 | + .build()) |
| 748 | + .build(); |
| 749 | + |
| 750 | + ImmutableMap<BidiReadObjectRequest, BidiReadObjectResponse> db = |
| 751 | + ImmutableMap.<BidiReadObjectRequest, BidiReadObjectResponse>builder() |
| 752 | + .put(req1, res1) |
| 753 | + .put(req2, res2) |
| 754 | + .put(req3, res3) |
| 755 | + .buildOrThrow(); |
| 756 | + |
| 757 | + runTestAgainstFakeServer(expected, db, ByteRangeSpec.relativeLength(10L, 20L)); |
| 758 | + } |
| 759 | + |
| 760 | + private void runTestAgainstFakeServer( |
| 761 | + ChecksummedTestContent expected, |
| 762 | + ImmutableMap<BidiReadObjectRequest, BidiReadObjectResponse> db, |
| 763 | + ByteRangeSpec range) |
| 764 | + throws Exception { |
| 765 | + |
| 766 | + StorageImplBase fake = |
| 767 | + new StorageImplBase() { |
| 768 | + @Override |
| 769 | + public StreamObserver<BidiReadObjectRequest> bidiReadObject( |
| 770 | + StreamObserver<BidiReadObjectResponse> respond) { |
| 771 | + return new StreamObserver<BidiReadObjectRequest>() { |
| 772 | + @Override |
| 773 | + public void onNext(BidiReadObjectRequest req) { |
| 774 | + if (db.containsKey(req)) { |
| 775 | + respond.onNext(db.get(req)); |
| 776 | + } else { |
| 777 | + respond.onError(TestUtils.apiException(Code.UNIMPLEMENTED, "Unexpected request")); |
| 778 | + } |
| 779 | + } |
| 780 | + |
| 781 | + @Override |
| 782 | + public void onError(Throwable t) { |
| 783 | + respond.onError(t); |
| 784 | + } |
| 785 | + |
| 786 | + @Override |
| 787 | + public void onCompleted() { |
| 788 | + respond.onCompleted(); |
| 789 | + } |
| 790 | + }; |
| 791 | + } |
| 792 | + }; |
| 793 | + |
| 794 | + try (FakeServer fakeServer = FakeServer.of(fake); |
| 795 | + Storage storage = fakeServer.getGrpcStorageOptions().getService()) { |
| 796 | + |
| 797 | + BlobId id = BlobId.of("b", "o"); |
| 798 | + ApiFuture<BlobDescriptor> futureObjectDescriptor = storage.getBlobDescriptor(id); |
| 799 | + |
| 800 | + try (BlobDescriptor bd = futureObjectDescriptor.get(5, TimeUnit.SECONDS)) { |
| 801 | + ApiFuture<byte[]> future = bd.readRangeAsBytes(range); |
| 802 | + |
| 803 | + byte[] actual = future.get(5, TimeUnit.SECONDS); |
| 804 | + Crc32cLengthKnown actualCrc32c = Hasher.enabled().hash(ByteBuffer.wrap(actual)); |
| 805 | + |
| 806 | + byte[] expectedBytes = expected.getBytes(); |
| 807 | + Crc32cLengthKnown expectedCrc32c = |
| 808 | + Crc32cValue.of(expected.getCrc32c(), expectedBytes.length); |
| 809 | + |
| 810 | + assertAll( |
| 811 | + () -> assertThat(actual).hasLength(expectedBytes.length), |
| 812 | + () -> assertThat(xxd(actual)).isEqualTo(xxd(expectedBytes)), |
| 813 | + () -> assertThat(actualCrc32c).isEqualTo(expectedCrc32c)); |
| 814 | + } |
| 815 | + } |
| 816 | + } |
| 817 | + |
| 818 | + private ReadRange getReadRange(int readId, int readOffset, ChecksummedTestContent content) { |
| 819 | + return getReadRange(readId, readOffset, content.asChecksummedData().getContent().size()); |
| 820 | + } |
| 821 | + |
641 | 822 | private static ReadRange getReadRange(int readId, int readOffset, int readLimit) { |
642 | 823 | return ReadRange.newBuilder() |
643 | 824 | .setReadId(readId) |
|
0 commit comments